Cómo conservar las referencias y administrar las referencias circulares con System.Text.Json

Para conservar las referencias y administrar las referencias circulares, establezca ReferenceHandler en Preserve. Este valor produce el comportamiento siguiente:

  • Al serializar:

    Al escribir tipos complejos, el serializador también escribe propiedades de metadatos ($id, $values y $ref).

  • Al deserializar:

    Se esperan metadatos (aunque no es obligatorio) y el deserializador intenta entenderlo.

En el siguiente código se muestra el uso del parámetro Preserve.

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

Esta característica no se puede usar para conservar tipos de valor o tipos inmutables. En la deserialización, se crea la instancia de un tipo inmutable después de leer la carga completa. Por lo tanto, sería imposible deserializar la misma instancia si aparece una referencia a ella dentro de la carga de JSON.

En el caso de los tipos de valor, los tipos inmutables y las matrices, no se serializan los metadatos de referencia. En la deserialización, se produce una excepción si se encuentra $ref o $id. Sin embargo, los tipos de valor omiten $id (y $values en el caso de las colecciones) para que sea posible deserializar las cargas que se serializaron mediante Newtonsoft.Json. Newtonsoft.Json serializa los metadatos de estos tipos.

Para determinar si los objetos son iguales, System.Text.Json usa ReferenceEqualityComparer.Instance, que usa la igualdad de referencia (Object.ReferenceEquals(Object, Object)) en lugar de la igualdad de valores (Object.Equals(Object) cuando se comparan dos instancias de objeto.

Para obtener más información sobre cómo se serializan y deserializan las referencias, vea ReferenceHandler.Preserve.

La clase ReferenceResolver define el comportamiento de conservar las referencias en la serialización y deserialización. Cree una clase derivada para especificar el comportamiento personalizado. Para obtener un ejemplo, vea GuidReferenceResolver.

System.Text.Json en .NET Core 3.1 solo admite la serialización por valor y produce una excepción para las referencias circulares.

Vea también