içinde başvuruları koruma ve döngüsel başvuruları işleme veya yoksayma System.Text.Json

Bu makalede. .NET'te JSON'u serileştirmek ve seri durumdan seri durumdan etmek için kullanırken başvuruları koruma ve döngüsel başvuruları işleme veya System.Text.Json yoksayma ile ilgili nasıl bir durum olduğu açıklanmıştır

Başvuruları koruma ve döngüsel başvuruları işleme

Başvuruları korumak ve döngüsel başvuruları işlemek için olarak ReferenceHandler Preserve ayarlayın. Bu ayar aşağıdaki davranışa neden olur:

  • Serileştirmede:

    Karmaşık türler yazarken seri hale getirici meta veri özelliklerini de yazar ( $id , $values ve $ref ).

  • Deserialize'da:

    Meta veriler beklenir (zorunlu değildir) ve deserializer bunu anlamaya çalışır.

Aşağıdaki kod, ayarın kullanımını Preserve göstermektedir.

using System;
using System.Collections.Generic;
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 = new List<Employee> { 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

Bu özellik, değer türlerini veya sabit türleri korumak için kullanılamaz. Deserialization'da, yükün tamamı okunduktan sonra sabit bir tür örneği oluşturulur. Bu nedenle, JSON yükü içinde bir başvuru görünürse aynı örneğinin deserialize olması mümkün olmayacaktır.

Değer türleri, sabit türler ve diziler için başvuru meta verileri seri hale getirilemez. Veya bulunursa, deserialization'da $ref bir özel durum $id oluşturur. Ancak, değer türleri kullanılarak seri hale getirilen yüklerin seri durumdan kaldırabilirsiniz (ve koleksiyonlar $id $values durumunda) yoksayabilirsiniz. Newtonsoft.Json Newtonsoft.Json , bu türler için meta verileri seri hale getirmez.

Nesnelerin eşit olup olmadığını belirlemek için, iki nesne örneğini karşılaştırırken değer eşitliği yerine başvuru eşitliğini System.Text.Json ReferenceEqualityComparer.Instance ( ) kullanan Object.ReferenceEquals(Object, Object) Object.Equals(Object) kullanır.

Başvuruların seri hale nasıl getirilecek ve seriden nasıl kaldır olduğu hakkında daha fazla bilgi için bkz. ReferenceHandler.Preserve .

ReferenceResolversınıfı, serileştirme ve seriden kaldıran başvurular koruma davranışını tanımlar. Özel davranış belirtmek için türetilmiş bir sınıf oluşturun. Bir örnek için bkz. GuidReferenceResolver.

Başvuru meta verilerini birden çok serileştirme ve seriyi kaldır çağrısında kalıcı hale getirme

Varsayılan olarak, başvuru verileri yalnızca veya çağrısı için önbelleğe Serialize alınmış Deserialize olur. Bir çağrıdan diğerine Serialize / Deserialize yapılan başvuruları kalıcı yapmak için, çağrısı ReferenceResolver sitesinde örneğine kök dizine Serialize / Deserialize bakın. Aşağıdaki kod, bu senaryo için bir örnek gösterir:

  • listesiniz var Employee ve her birini tek tek seri hale getirmemiz gerekir.
  • çözümleyicisinde kaydedilen başvurulardan ReferenceHandler yararlanmak istediğiniz.

Sınıfı şu Employee şekildedir:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }
    public List<Employee> DirectReports { get; set; }
}

'den türeten ReferenceResolver bir sınıf, başvuruları bir sözlükte depolar:

class MyReferenceResolver : ReferenceResolver
{
    private uint _referenceCount;
    private readonly Dictionary<string, object> _referenceIdToObjectMap = new ();
    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;
    }
}

sınıfından türeten bir sınıf, bir örneğini tutar ve yalnızca gerektiğinde yeni bir örnek oluşturur ReferenceHandler MyReferenceResolver (bu örnekte adlı Reset bir yöntemde):

class MyReferenceHandler : ReferenceHandler
{
    public MyReferenceHandler() => Reset();
    private ReferenceResolver _rootedResolver;
    public override ReferenceResolver CreateResolver() => _rootedResolver;
    public void Reset() => _rootedResolver = new MyReferenceResolver();
}

Örnek kod seri hale getiriciyi çağırarak özelliğinin bir JsonSerializerOptions ReferenceHandler örneğine ayar olduğu bir örneği MyReferenceHandler kullanır. Bu düzeni izledikten sonra, sonsuza kadar büyümesine karşı serileştirmeyi bitirdikten sonra sözlüğü ReferenceResolver sıfırlamayı unutmayın.

var options = new JsonSerializerOptions();
options.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();

System.Text.Json .NET Core 3.1'de yalnızca değere göre serileştirmeyi destekler ve döngüsel başvurular için bir özel durum oluşturur.

Döngüsel başvuruları yoksayma

Döngüsel başvuruları işleme yerine yoksayabilirsiniz. Döngüsel başvuruları yoksaymak için olarak ReferenceHandler IgnoreCycles ayarlayın. Seri hale getirici, aşağıdaki örnekte gösterildiği null gibi döngüsel başvuru özelliklerini olarak ayarlar:

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

Yukarıdaki örnekte, Manager döngüsel Adrian King başvuruyu önlemek için altında null serileştirilmiştir. Bu davranış, aşağıdaki avantajlara ReferenceHandler.Preserve sahiptir:

  • Yük boyutunu azaltıyor.
  • ve dışında seri hale getiriciler için anlanabilir JSON System.Text.Json Newtonsoft.Json oluşturur.

Bu davranışın aşağıdaki dezavantajları vardır:

  • Sessiz veri kaybı.
  • Veriler JSON'dan kaynak nesneye gidiş dönüş kullanılamaz.

System.Text.Json .NET 5 ve önceki sürümlerinde ReferenceHandler.IgnoreCycles desteklemez.

Ayrıca bkz.