A hivatkozások megőrzése és a körkörös hivatkozások kezelése vagy figyelmen kívül hagyása a System.Text.Json
Ez a cikk bemutatja, hogyan őrizheti meg a hivatkozásokat, és hogyan kezelheti vagy figyelmen kívül hagyhatja a körkörös hivatkozásokat a JSON szerializálása és deszerializálása során System.Text.Json a .NET-ben
Hivatkozások megőrzése és körkörös hivatkozások kezelése
A hivatkozások megőrzéséhez és a körkörös hivatkozások kezeléséhez állítsa a következőre ReferenceHandler : Preserve. Ez a beállítás a következő viselkedést okozza:
Szerializáláskor:
Összetett típusok írásakor a szerializáló metaadat-tulajdonságokat (
$id
és$values
$ref
) is ír.Deszerializáláskor:
A metaadatok várhatók (bár nem kötelezőek), és a deszerializáló megpróbálja megérteni.
Az alábbi kód a beállítás használatát Preserve
mutatja be.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace PreserveReferences
{
public class Employee
{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
}
public class Program
{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = [adrian];
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
Console.WriteLine($"Tyler serialized:\n{tylerJson}");
Employee? tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized?.DirectReports?[0].Manager == tylerDeserialized);
}
}
}
// Produces output like the following example:
//
//Tyler serialized:
//{
// "$id": "1",
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": {
// "$id": "2",
// "$values": [
// {
// "$id": "3",
// "Name": "Adrian King",
// "Manager": {
// "$ref": "1"
// },
// "DirectReports": null
// }
// ]
// }
//}
//Tyler is manager of Tyler's first direct report:
//True
Imports System.Text.Json
Imports System.Text.Json.Serialization
Namespace PreserveReferences
Public Class Employee
Public Property Name As String
Public Property Manager As Employee
Public Property DirectReports As List(Of Employee)
End Class
Public NotInheritable Class Program
Public Shared Sub Main()
Dim tyler As New Employee
Dim adrian As New Employee
tyler.DirectReports = New List(Of Employee) From {
adrian}
adrian.Manager = tyler
Dim options As New JsonSerializerOptions With {
.ReferenceHandler = ReferenceHandler.Preserve,
.WriteIndented = True
}
Dim tylerJson As String = JsonSerializer.Serialize(tyler, options)
Console.WriteLine($"Tyler serialized:{tylerJson}")
Dim tylerDeserialized As Employee = JsonSerializer.Deserialize(Of Employee)(tylerJson, options)
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ")
Console.WriteLine(
tylerDeserialized.DirectReports(0).Manager Is tylerDeserialized)
End Sub
End Class
End Namespace
' Produces output like the following example:
'
'Tyler serialized:
'{
' "$id": "1",
' "Name": "Tyler Stein",
' "Manager": null,
' "DirectReports": {
' "$id": "2",
' "$values": [
' {
' "$id": "3",
' "Name": "Adrian King",
' "Manager": {
' "$ref": "1"
' },
' "DirectReports": null
' }
' ]
' }
'}
'Tyler is manager of Tyler's first direct report:
'True
Ez a funkció nem használható értéktípusok vagy nem módosítható típusok megőrzésére. Deszerializáláskor a rendszer egy nem módosítható típusú példányt hoz létre a teljes hasznos adat beolvasása után. Így lehetetlen lenne deszerializálni ugyanazt a példányt, ha egy hivatkozás jelenik meg a JSON hasznos adatai között.
Értéktípusok, nem módosítható típusok és tömbök esetén a rendszer nem szerializálja a referencia-metaadatokat. A deszerializáláskor kivételt ad a rendszer, ha $ref
megtalálható vagy $id
megtalálható. Az értéktípusok azonban figyelmen kívül hagyják $id
(és $values
gyűjtemények esetén), hogy lehetővé tegyék a szerializált hasznos adatok deszerializálását a használatával Newtonsoft.Json. Newtonsoft.Json szerializálja az ilyen típusú metaadatokat.
Annak megállapításához, hogy az objektumok egyenlőek-e, System.Text.Json a két objektumpéldány összehasonlítása során az értékegyenlőség () helyett hivatkozási egyenlőséget (Object.ReferenceEquals(Object, Object)Object.Equals(Object)) használReferenceEqualityComparer.Instance.
További információ a hivatkozások szerializálásáról és deszerializálásáról: ReferenceHandler.Preserve.
Az ReferenceResolver osztály meghatározza a szerializálásra és deszerializálásra vonatkozó hivatkozások megőrzésének viselkedését. Hozzon létre egy származtatott osztályt az egyéni viselkedés megadásához. Példa: GuidReferenceResolver.
Hivatkozási metaadatok megőrzése több szerializálási és deszerializálási hívásban
Alapértelmezés szerint a referenciaadatok csak az egyes hívásokhoz vagy hívásokhoz gyorsítótárazva lesznek SerializeDeserialize. Ha meg szeretné őrizni a hivatkozásokat az egyik Serialize
/Deserialize
hívásból a másikba, gyökereztetheti a ReferenceResolver példányt a híváswebhelyen.Serialize
/Deserialize
Az alábbi kód egy példát mutat be erre a forgatókönyvre:
- Van egy lista az
Employee
objektumokról, és egyenként kell szerializálnia azokat. - Érdemes kihasználni a feloldóban mentett hivatkozásokat a
ReferenceHandler
következőhöz: .
Itt van az Employee
osztály:
public class Employee
{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
}
Egy olyan osztály, amely a hivatkozások szótárban való tárolásából ReferenceResolver származik:
class MyReferenceResolver : ReferenceResolver
{
private uint _referenceCount;
private readonly Dictionary<string, object> _referenceIdToObjectMap = [];
private readonly Dictionary<object, string> _objectToReferenceIdMap = new (ReferenceEqualityComparer.Instance);
public override void AddReference(string referenceId, object value)
{
if (!_referenceIdToObjectMap.TryAdd(referenceId, value))
{
throw new JsonException();
}
}
public override string GetReference(object value, out bool alreadyExists)
{
if (_objectToReferenceIdMap.TryGetValue(value, out string? referenceId))
{
alreadyExists = true;
}
else
{
_referenceCount++;
referenceId = _referenceCount.ToString();
_objectToReferenceIdMap.Add(value, referenceId);
alreadyExists = false;
}
return referenceId;
}
public override object ResolveReference(string referenceId)
{
if (!_referenceIdToObjectMap.TryGetValue(referenceId, out object? value))
{
throw new JsonException();
}
return value;
}
}
Egy osztály, amely egy példányból ReferenceHandlerMyReferenceResolver
származik, és csak szükség esetén hoz létre új példányt (az ebben a példában elnevezett Reset
metódusban):
class MyReferenceHandler : ReferenceHandler
{
public MyReferenceHandler() => Reset();
private ReferenceResolver? _rootedResolver;
public override ReferenceResolver CreateResolver() => _rootedResolver!;
public void Reset() => _rootedResolver = new MyReferenceResolver();
}
Amikor a mintakód meghívja a szerializálót, egy olyan példányt JsonSerializerOptions használ, amelyben a ReferenceHandler tulajdonság egy példányra MyReferenceHandler
van állítva. Ha követi ezt a mintát, mindenképpen állítsa alaphelyzetbe a szótárat, amikor végzett a ReferenceResolver
szerializálással, hogy ne nőjön örökké.
var options = new JsonSerializerOptions
{
WriteIndented = true
};
var myReferenceHandler = new MyReferenceHandler();
options.ReferenceHandler = myReferenceHandler;
string json;
foreach (Employee emp in employees)
{
json = JsonSerializer.Serialize(emp, options);
DoSomething(json);
}
// Reset after serializing to avoid out of bounds memory growth in the resolver.
myReferenceHandler.Reset();
Körkörös hivatkozások figyelmen kívül hagyása
A körkörös hivatkozások kezelése helyett figyelmen kívül hagyhatja őket. A körkörös hivatkozások figyelmen kívül hagyásához állítsa a ReferenceHandler következőre: IgnoreCycles. A szerializáló a körkörös referenciatulajdonságokat null
a következő példában látható módon állítja be:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SerializeIgnoreCycles
{
public class Employee
{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
}
public class Program
{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
Console.WriteLine($"Tyler serialized:\n{tylerJson}");
Employee? tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized?.DirectReports?[0]?.Manager == tylerDeserialized);
}
}
}
// Produces output like the following example:
//
//Tyler serialized:
//{
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": [
// {
// "Name": "Adrian King",
// "Manager": null,
// "DirectReports": null
// }
// ]
//}
//Tyler is manager of Tyler's first direct report:
//False
Az előző példában Manager
az alatt Adrian King
a körkörös hivatkozás elkerülése érdekében szerializálva null
van. Ennek a viselkedésnek a következő előnyei vannak ReferenceHandler.Preserve:
- Csökkenti a hasznos adatok méretét.
- Olyan JSON-t hoz létre, amely nem és Newtonsoft.Jsonnem System.Text.Json szerializálók számára érthető.
Ennek a viselkedésnek a következő hátrányai vannak:
- Csendes adatvesztés.
- Az adatok nem tudnak visszafordulni a JSON-ból a forrásobjektumba.
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: