Megosztás a következőn keresztül:


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 ReferenceHandlerkö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 MyReferenceHandlervan á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 nulla 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