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

Bu makalede, .NET'te JSON'ı seri hale getirmek ve seri durumdan çıkarmak için kullanırken System.Text.Json başvuruları koruma ve döngüsel başvuruları işleme veya yoksayma işlemleri gösterilmektedir

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 PreserveayarlayınReferenceHandler. Bu ayar aşağıdaki davranışa neden olur:

  • Serileştirmede:

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

  • Seri durumdan çıkarmada:

    Meta veriler beklenir (zorunlu olmasa da) ve seri durumdan çıkarıcı bunu anlamaya çalışır.

Aşağıdaki kodda ayarın kullanımı gösterilmektedir Preserve .

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

Bu özellik, değer türlerini veya sabit türleri korumak için kullanılamaz. Seri durumdan çıkarma işleminde, yükün tamamı okunduktan sonra sabit bir tür örneği oluşturulur. Bu nedenle, JSON yükünde bir başvuru görünürse aynı örneği seri durumdan çıkarmak mümkün olmaz.

Değer türleri, sabit türler ve diziler için başvuru meta verileri serileştirilmez. Seri durumdan çıkarmada, veya $id bulunursa $ref bir özel durum oluşturulur. Ancak, değer türleri kullanılarak serileştirilmiş Newtonsoft.Jsonyüklerin seri durumdan çıkarılmalarını sağlamak için (ve $values koleksiyonlar söz konusu olduğunda) yoksayar $id ve bu tür türlerin meta verilerini seri hale getirir.

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

Başvuruların nasıl seri hale getirildiği ve seri durumdan çıkarıldığı hakkında daha fazla bilgi için bkz ReferenceHandler.Preserve. .

sınıfı, ReferenceResolver serileştirme ve seri durumdan çıkarmada başvuruları koruma davranışını tanımlar. Özel davranışı belirtmek için türetilmiş bir sınıf oluşturun. Örnek için bkz . GuidReferenceResolver.

Birden çok serileştirme ve seri durumdan çıkarma çağrısında başvuru meta verilerini kalıcı hale getirme

Varsayılan olarak, başvuru verileri yalnızca veya çağrısı için önbelleğe Serialize Deserializealınır. Bir veya başka bir Serialize Deserialize çağrıdan başvuruları kalıcı hale getirmek için, çağrısı sitesindeki örneğin kökünü ReferenceResolver Serialize/Deserializeoluşturun. Aşağıdaki kodda bu senaryo için bir örnek gösterilmektedir:

  • Nesnelerin bir listesi Employee vardır ve her birini ayrı ayrı seri hale getirmeniz gerekir.
  • için ReferenceHandlerçözümleyicide kaydedilen başvurulardan yararlanmak istiyorsunuz.

Sınıfı aşağıdadır Employee :

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

Başvurulardan ReferenceResolver türetilen bir sınıf, başvuruları bir sözlükte depolar:

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;
    }
}

öğesinden ReferenceHandler türetilen bir sınıf, örneğini MyReferenceResolver barındırıyor ve yalnızca gerektiğinde yeni bir örnek oluşturuyor (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ğırdığında, özelliğinin ReferenceHandler bir JsonSerializerOptions örneğine ayarlandığı bir örneği MyReferenceHandlerkullanır. Bu düzeni uyguladığınızda, serileştirmeyi bitirdiğinizde sözlüğü sıfırladığınızdan ReferenceResolver emin olun ve sonsuza kadar büyümesini engelleyebilirsiniz.

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();

Döngüsel başvuruları yoksay

Döngüsel başvuruları işlemek yerine bunları yoksayabilirsiniz. Döngüsel başvuruları yoksaymak için olarak IgnoreCyclesayarlayınReferenceHandler. Seri hale getirici, aşağıdaki örnekte gösterildiği gibi döngüsel başvuru özelliklerini nullolarak 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 başvuruyu önlemek için altında Adrian King olarak serileştirilmiştir null . Bu davranış aşağıdaki avantajlara ReferenceHandler.Preservesahiptir:

  • Yük boyutunu azaltır.
  • ve Newtonsoft.Jsondışındaki System.Text.Json seri hale getiriciler için anlaşılır JSON oluşturur.

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

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

Ayrıca bkz.