Uso de un modelo de objetos de documento JSON en System.Text.Json

En este artículo se explica cómo usar un modelo de objetos de documento (DOM) JSON para acceder aleatoriamente a los datos de una carga JSON.

Elecciones de DOM JSON

Usar un DOM es una alternativa a la deserialización con JsonSerializer en las siguientes situaciones:

  • No existe un tipo en el que deserializar.
  • El JSON que se recibe no tiene un esquema fijo y hay que inspeccionarlo para saber lo que contiene.

System.Text.Json proporciona dos maneras de compilar un DOM JSON:

  • JsonDocument ofrece la posibilidad de compilar un DOM de solo lectura mediante Utf8JsonReader. A los elementos JSON que componen la carga se puede acceder mediante el tipo JsonElement. El tipo JsonElement contiene los enumeradores de matriz y objeto junto con las API para convertir texto JSON en tipos de .NET comunes. JsonDocument expone una propiedad RootElement. Para obtener más información, vea Uso de JsonDocument más adelante en este artículo.

  • JsonNode y las clases que derivan de él en el espacio de nombres System.Text.Json.Nodes ofrecen la posibilidad de crear un DOM mutable. A los elementos JSON que componen la carga se puede acceder mediante los tipos JsonNode, JsonObject, JsonArray, JsonValue y JsonElement. Para obtener más información, vea Uso de JsonNode más adelante en este artículo.

Tenga en cuenta los siguientes factores a la hora de elegir entre JsonDocument y JsonNode:

  • El DOM JsonNode se puede cambiar después de su creación. El DOM JsonDocument es inmutable.
  • El DOM JsonDocument proporciona un acceso más rápido a sus datos.

Use JsonNode

En el ejemplo siguiente se muestra cómo usar JsonNode y los otros tipos del espacio de nombres System.Text.Json.Nodes para:

  • Crear un DOM a partir de una cadena JSON
  • Escribir JSON desde un DOM.
  • Obtener un valor, un objeto o una matriz de un DOM.
using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodeFromStringExample;

public class Program
{
    public static void Main()
    {
        string jsonString = """
            {
              "Date": "2019-08-01T00:00:00",
              "Temperature": 25,
              "Summary": "Hot",
              "DatesAvailable": [
                "2019-08-01T00:00:00",
                "2019-08-02T00:00:00"
              ],
              "TemperatureRanges": {
                  "Cold": {
                      "High": 20,
                      "Low": -10
                  },
                  "Hot": {
                      "High": 60,
                      "Low": 20
                  }
              }
            }
            """;
        // Create a JsonNode DOM from a JSON string.
        JsonNode forecastNode = JsonNode.Parse(jsonString)!;

        // Write JSON from a JsonNode
        var options = new JsonSerializerOptions { WriteIndented = true };
        Console.WriteLine(forecastNode!.ToJsonString(options));
        // output:
        //{
        //  "Date": "2019-08-01T00:00:00",
        //  "Temperature": 25,
        //  "Summary": "Hot",
        //  "DatesAvailable": [
        //    "2019-08-01T00:00:00",
        //    "2019-08-02T00:00:00"
        //  ],
        //  "TemperatureRanges": {
        //    "Cold": {
        //      "High": 20,
        //      "Low": -10
        //    },
        //    "Hot": {
        //      "High": 60,
        //      "Low": 20
        //    }
        //  }
        //}

        // Get value from a JsonNode.
        JsonNode temperatureNode = forecastNode!["Temperature"]!;
        Console.WriteLine($"Type={temperatureNode.GetType()}");
        Console.WriteLine($"JSON={temperatureNode.ToJsonString()}");
        //output:
        //Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
        //JSON = 25

        // Get a typed value from a JsonNode.
        int temperatureInt = (int)forecastNode!["Temperature"]!;
        Console.WriteLine($"Value={temperatureInt}");
        //output:
        //Value=25

        // Get a typed value from a JsonNode by using GetValue<T>.
        temperatureInt = forecastNode!["Temperature"]!.GetValue<int>();
        Console.WriteLine($"TemperatureInt={temperatureInt}");
        //output:
        //Value=25

        // Get a JSON object from a JsonNode.
        JsonNode temperatureRanges = forecastNode!["TemperatureRanges"]!;
        Console.WriteLine($"Type={temperatureRanges.GetType()}");
        Console.WriteLine($"JSON={temperatureRanges.ToJsonString()}");
        //output:
        //Type = System.Text.Json.Nodes.JsonObject
        //JSON = { "Cold":{ "High":20,"Low":-10},"Hot":{ "High":60,"Low":20} }

        // Get a JSON array from a JsonNode.
        JsonNode datesAvailable = forecastNode!["DatesAvailable"]!;
        Console.WriteLine($"Type={datesAvailable.GetType()}");
        Console.WriteLine($"JSON={datesAvailable.ToJsonString()}");
        //output:
        //datesAvailable Type = System.Text.Json.Nodes.JsonArray
        //datesAvailable JSON =["2019-08-01T00:00:00", "2019-08-02T00:00:00"]

        // Get an array element value from a JsonArray.
        JsonNode firstDateAvailable = datesAvailable[0]!;
        Console.WriteLine($"Type={firstDateAvailable.GetType()}");
        Console.WriteLine($"JSON={firstDateAvailable.ToJsonString()}");
        //output:
        //Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
        //JSON = "2019-08-01T00:00:00"

        // Get a typed value by chaining references.
        int coldHighTemperature = (int)forecastNode["TemperatureRanges"]!["Cold"]!["High"]!;
        Console.WriteLine($"TemperatureRanges.Cold.High={coldHighTemperature}");
        //output:
        //TemperatureRanges.Cold.High = 20

        // Parse a JSON array
        var datesNode = JsonNode.Parse(@"[""2019-08-01T00:00:00"",""2019-08-02T00:00:00""]");
        JsonNode firstDate = datesNode![0]!.GetValue<DateTime>();
        Console.WriteLine($"firstDate={ firstDate}");
        //output:
        //firstDate = "2019-08-01T00:00:00"
    }
}

Creación de un DOM JsonNode con inicializadores de objeto y realización de cambios

El ejemplo siguiente muestra cómo:

  • Crear un DOM mediante inicializadores de objeto.
  • Realizar cambios en un DOM.
using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodeFromObjectExample;

public class Program
{
    public static void Main()
    {
        // Create a new JsonObject using object initializers.
        var forecastObject = new JsonObject
        {
            ["Date"] = new DateTime(2019, 8, 1),
            ["Temperature"] = 25,
            ["Summary"] = "Hot",
            ["DatesAvailable"] = new JsonArray(
                new DateTime(2019, 8, 1), new DateTime(2019, 8, 2)),
            ["TemperatureRanges"] = new JsonObject
            {
                ["Cold"] = new JsonObject
                {
                    ["High"] = 20,
                    ["Low"] = -10
                }
            },
            ["SummaryWords"] = new JsonArray("Cool", "Windy", "Humid")
        };

        // Add an object.
        forecastObject!["TemperatureRanges"]!["Hot"] =
            new JsonObject { ["High"] = 60, ["Low"] = 20 };

        // Remove a property.
        forecastObject.Remove("SummaryWords");

        // Change the value of a property.
        forecastObject["Date"] = new DateTime(2019, 8, 3);

        var options = new JsonSerializerOptions { WriteIndented = true };
        Console.WriteLine(forecastObject.ToJsonString(options));
        //output:
        //{
        //  "Date": "2019-08-03T00:00:00",
        //  "Temperature": 25,
        //  "Summary": "Hot",
        //  "DatesAvailable": [
        //    "2019-08-01T00:00:00",
        //    "2019-08-02T00:00:00"
        //  ],
        //  "TemperatureRanges": {
        //    "Cold": {
        //      "High": 20,
        //      "Low": -10
        //    },
        //    "Hot": {
        //      "High": 60,
        //      "Low": 20
        //    }
        //  }
        //}
    }
}

Deserialización de subsecciones de una carga JSON

En el ejemplo siguiente se muestra cómo usar JsonNode para ir a una subsección de un árbol JSON y deserializar un único valor, un tipo personalizado o una matriz de esa subsección.

using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodePOCOExample;

public class TemperatureRanges : Dictionary<string, HighLowTemps>
{
}

public class HighLowTemps
{
    public int High { get; set; }
    public int Low { get; set; }
}

public class Program
{
    public static DateTime[]? DatesAvailable { get; set; }

    public static void Main()
    {
        string jsonString = """
            {
              "Date": "2019-08-01T00:00:00",
              "Temperature": 25,
              "Summary": "Hot",
              "DatesAvailable": [
                "2019-08-01T00:00:00",
                "2019-08-02T00:00:00"
              ],
              "TemperatureRanges": {
                  "Cold": {
                      "High": 20,
                      "Low": -10
                  },
                  "Hot": {
                      "High": 60,
                      "Low": 20
                  }
              }
            }
            """;
        // Parse all of the JSON.
        JsonNode forecastNode = JsonNode.Parse(jsonString)!;

        // Get a single value
        int hotHigh = forecastNode["TemperatureRanges"]!["Hot"]!["High"]!.GetValue<int>();
        Console.WriteLine($"Hot.High={hotHigh}");
        // output:
        //Hot.High=60

        // Get a subsection and deserialize it into a custom type.
        JsonObject temperatureRangesObject = forecastNode!["TemperatureRanges"]!.AsObject();
        using var stream = new MemoryStream();
        using var writer = new Utf8JsonWriter(stream);
        temperatureRangesObject.WriteTo(writer);
        writer.Flush();
        TemperatureRanges? temperatureRanges = 
            JsonSerializer.Deserialize<TemperatureRanges>(stream.ToArray());
        Console.WriteLine($"Cold.Low={temperatureRanges!["Cold"].Low}, Hot.High={temperatureRanges["Hot"].High}");
        // output:
        //Cold.Low=-10, Hot.High=60

        // Get a subsection and deserialize it into an array.
        JsonArray datesAvailable = forecastNode!["DatesAvailable"]!.AsArray()!;
        Console.WriteLine($"DatesAvailable[0]={datesAvailable[0]}");
        // output:
        //DatesAvailable[0]=8/1/2019 12:00:00 AM
    }
}

Ejemplo de nota media de JsonNode

En el ejemplo siguiente se selecciona una matriz JSON que tiene valores enteros y se calcula un valor medio:

using System.Text.Json.Nodes;

namespace JsonNodeAverageGradeExample;

public class Program
{
    public static void Main()
    {
        string jsonString = """
            {
              "Class Name": "Science",
              "Teacher\u0027s Name": "Jane",
              "Semester": "2019-01-01",
              "Students": [
                {
                  "Name": "John",
                  "Grade": 94.3
                },
                {
                  "Name": "James",
                  "Grade": 81.0
                },
                {
                  "Name": "Julia",
                  "Grade": 91.9
                },
                {
                  "Name": "Jessica",
                  "Grade": 72.4
                },
                {
                  "Name": "Johnathan"
                }
              ],
              "Final": true
            }
            """;
        double sum = 0;
        JsonNode document = JsonNode.Parse(jsonString)!;

        JsonNode root = document.Root;
        JsonArray studentsArray = root["Students"]!.AsArray();

        int count = studentsArray.Count;
        foreach (JsonNode? student in studentsArray)
        {
            if (student?["Grade"] is JsonNode gradeNode)
            {
                sum += (double)gradeNode;
            }
            else
            {
                sum += 70;
            }
        }

        double average = sum / count;
        Console.WriteLine($"Average grade : {average}");
    }
}
// output:
//Average grade : 81.92

El código anterior:

  • Calcula la calificación media de los objetos de una matriz de Students que tienen una propiedad Grade.
  • Asigna una calificación predeterminada de 70 a los alumnos que no tienen ninguna calificación.
  • Obtiene el número de alumnos de la propiedad Count de JsonArray.

JsonNode con JsonSerializerOptions

Puede usar JsonSerializer para serializar y deserializar una instancia de JsonNode. Pero si usa una sobrecarga que toma JsonSerializerOptions, la instancia de opciones solo se usa para obtener convertidores personalizados. No se usan otras características de la instancia de opciones. Por ejemplo, si establece JsonSerializerOptions.DefaultIgnoreCondition en WhenWritingNull y llama a JsonSerializer con una sobrecarga que toma JsonSerializerOptions, no se omiten las propiedades null.

La misma limitación se aplica a los métodos JsonNode que toman un parámetro JsonSerializerOptions: WriteTo(Utf8JsonWriter, JsonSerializerOptions) y ToJsonString(JsonSerializerOptions). Estas API solo usan JsonSerializerOptions para obtener convertidores personalizados.

En el ejemplo siguiente se muestra el resultado del uso de métodos que toman un parámetro JsonSerializerOptions y serializan una instancia JsonNode:

using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace JsonNodeWithJsonSerializerOptions;

public class Program
{
    public static void Main()
    {
        Person person = new() { Name = "Nancy" };

        // Default serialization - Address property included with null token.
        // Output: {"Name":"Nancy","Address":null}
        string personJsonWithNull = JsonSerializer.Serialize(person);
        Console.WriteLine(personJsonWithNull);

        // Serialize and ignore null properties - null Address property is omitted
        // Output: {"Name":"Nancy"}
        JsonSerializerOptions options = new()
        {
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
        };
        string personJsonWithoutNull = JsonSerializer.Serialize(person, options);
        Console.WriteLine(personJsonWithoutNull);

        // Ignore null properties doesn't work when serializing JsonNode instance
        // by using JsonSerializer.
        // Output: {"Name":"Nancy","Address":null}
        JsonNode? personJsonNode = JsonSerializer.Deserialize<JsonNode>(personJsonWithNull);
        personJsonWithNull = JsonSerializer.Serialize(personJsonNode, options);
        Console.WriteLine(personJsonWithNull);

        // Ignore null properties doesn't work when serializing JsonNode instance
        // by using JsonNode.ToJsonString method.
        // Output: {"Name":"Nancy","Address":null}
        personJsonWithNull = personJsonNode!.ToJsonString(options);
        Console.WriteLine(personJsonWithNull);

        // Ignore null properties doesn't work when serializing JsonNode instance
        // by using JsonNode.WriteTo method.
        // Output: {"Name":"Nancy","Address":null}
        using var stream = new MemoryStream();
        using var writer = new Utf8JsonWriter(stream);
        personJsonNode!.WriteTo(writer, options);
        writer.Flush();
        personJsonWithNull = Encoding.UTF8.GetString(stream.ToArray());
        Console.WriteLine(personJsonWithNull);
    }
}

public class Person
{
    public string? Name { get; set; }
    public string? Address { get; set; }
}

Si necesita características de JsonSerializerOptions distintas a los convertidores personalizados, use JsonSerializer con destinos fuertemente tipados (como la clase Person de este ejemplo) en lugar de JsonNode.

Use JsonDocument

En el ejemplo siguiente se muestra cómo usar la clase JsonDocument para el acceso aleatorio a los datos de una cadena JSON:

double sum = 0;
int count = 0;

using (JsonDocument document = JsonDocument.Parse(jsonString))
{
    JsonElement root = document.RootElement;
    JsonElement studentsElement = root.GetProperty("Students");
    foreach (JsonElement student in studentsElement.EnumerateArray())
    {
        if (student.TryGetProperty("Grade", out JsonElement gradeElement))
        {
            sum += gradeElement.GetDouble();
        }
        else
        {
            sum += 70;
        }
        count++;
    }
}

double average = sum / count;
Console.WriteLine($"Average grade : {average}");
Dim sum As Double = 0
Dim count As Integer = 0
Using document As JsonDocument = JsonDocument.Parse(jsonString)
    Dim root As JsonElement = document.RootElement
    Dim studentsElement As JsonElement = root.GetProperty("Students")
    For Each student As JsonElement In studentsElement.EnumerateArray()
        Dim gradeElement As JsonElement = Nothing
        If student.TryGetProperty("Grade", gradeElement) Then
            sum += gradeElement.GetDouble()
        Else
            sum += 70
        End If
        count += 1
    Next
End Using

Dim average As Double = sum / count
Console.WriteLine($"Average grade : {average}")

El código anterior:

  • Supone que el JSON que se va a analizar está en una cadena denominada jsonString.
  • Calcula la calificación media de los objetos de una matriz de Students que tienen una propiedad Grade.
  • Asigna una calificación predeterminada de 70 a los alumnos que no tienen ninguna calificación.
  • Crea la instancia JsonDocument en using (Instrucción) porque JsonDocument implementa IDisposable. Después de eliminar una instancia JsonDocument, también se pierde el acceso a todas sus instancias JsonElement. Para conservar el acceso a una instancia JsonElement, realice una copia de ella antes de eliminar la instancia primaria JsonDocument. Para realizar una copia, llame a JsonElement.Clone. Para obtener más información, vea JsonDocument es IDisposable.

El código de ejemplo anterior cuenta a los alumnos por medio del incremento de una variable count con cada iteración. Una alternativa es llamar a GetArrayLength, tal y como se muestra en el ejemplo siguiente:

double sum = 0;
int count = 0;

using (JsonDocument document = JsonDocument.Parse(jsonString))
{
    JsonElement root = document.RootElement;
    JsonElement studentsElement = root.GetProperty("Students");

    count = studentsElement.GetArrayLength();

    foreach (JsonElement student in studentsElement.EnumerateArray())
    {
        if (student.TryGetProperty("Grade", out JsonElement gradeElement))
        {
            sum += gradeElement.GetDouble();
        }
        else
        {
            sum += 70;
        }
    }
}

double average = sum / count;
Console.WriteLine($"Average grade : {average}");
Dim sum As Double = 0
Dim count As Integer = 0
Using document As JsonDocument = JsonDocument.Parse(jsonString)
    Dim root As JsonElement = document.RootElement
    Dim studentsElement As JsonElement = root.GetProperty("Students")

    count = studentsElement.GetArrayLength()

    For Each student As JsonElement In studentsElement.EnumerateArray()
        Dim gradeElement As JsonElement = Nothing
        If student.TryGetProperty("Grade", gradeElement) Then
            sum += gradeElement.GetDouble()
        Else
            sum += 70
        End If
    Next
End Using

Dim average As Double = sum / count
Console.WriteLine($"Average grade : {average}")

Aquí se muestra un ejemplo del código JSON que este código procesa:

{
  "Class Name": "Science",
  "Teacher\u0027s Name": "Jane",
  "Semester": "2019-01-01",
  "Students": [
    {
      "Name": "John",
      "Grade": 94.3
    },
    {
      "Name": "James",
      "Grade": 81.0
    },
    {
      "Name": "Julia",
      "Grade": 91.9
    },
    {
      "Name": "Jessica",
      "Grade": 72.4
    },
    {
      "Name": "Johnathan"
    }
  ],
  "Final": true
}

Para obtener un ejemplo similar que usa JsonNode en lugar de JsonDocument, vea Ejemplo de nota media de JsonNode.

Cómo buscar subelementos en JsonDocument y JsonElement

Las búsquedas en JsonElement requieren una búsqueda secuencial de las propiedades y, por lo tanto, son relativamente lentas (por ejemplo, al usar TryGetProperty). System.Text.Json está diseñado para minimizar el tiempo de análisis inicial en lugar del tiempo de búsqueda. Por lo tanto, use los enfoques siguientes para optimizar el rendimiento al buscar en un objeto JsonDocument:

  • Use los enumeradores integrados (EnumerateArray y EnumerateObject) en lugar de crear sus propios bucles o índices.
  • No realice una búsqueda secuencial en todo el JsonDocument a través de todas las propiedades mediante RootElement. En su lugar, busque objetos JSON anidados en función de la estructura conocida de los datos JSON. Por ejemplo, en los ejemplos de código anteriores se busca una propiedad Grade en objetos Student al recorrer en bucle los objetos Student y obtener el valor de Grade de cada uno, en lugar de buscar en todos los objetos JsonElement las propiedades Grade. Si se hace esto último, se pasaría innecesariamente por los mismos datos.

Uso de JsonDocument para escribir JSON

En el siguiente ejemplo se muestra cómo escribir JSON desde una JsonDocument:

string jsonString = File.ReadAllText(inputFileName);

var writerOptions = new JsonWriterOptions
{
    Indented = true
};

var documentOptions = new JsonDocumentOptions
{
    CommentHandling = JsonCommentHandling.Skip
};

using FileStream fs = File.Create(outputFileName);
using var writer = new Utf8JsonWriter(fs, options: writerOptions);
using JsonDocument document = JsonDocument.Parse(jsonString, documentOptions);

JsonElement root = document.RootElement;

if (root.ValueKind == JsonValueKind.Object)
{
    writer.WriteStartObject();
}
else
{
    return;
}

foreach (JsonProperty property in root.EnumerateObject())
{
    property.WriteTo(writer);
}

writer.WriteEndObject();

writer.Flush();
Dim jsonString As String = File.ReadAllText(inputFileName)

Dim writerOptions As JsonWriterOptions = New JsonWriterOptions With {
    .Indented = True
}

Dim documentOptions As JsonDocumentOptions = New JsonDocumentOptions With {
    .CommentHandling = JsonCommentHandling.Skip
}

Dim fs As FileStream = File.Create(outputFileName)
Dim writer As Utf8JsonWriter = New Utf8JsonWriter(fs, options:=writerOptions)
Dim document As JsonDocument = JsonDocument.Parse(jsonString, documentOptions)

Dim root As JsonElement = document.RootElement

If root.ValueKind = JsonValueKind.[Object] Then
    writer.WriteStartObject()
Else
    Return
End If

For Each [property] As JsonProperty In root.EnumerateObject()
    [property].WriteTo(writer)
Next

writer.WriteEndObject()

writer.Flush()

El código anterior:

  • Lee un archivo JSON, carga los datos en un JsonDocument y escribe JSON con formato (impreso correctamente) en un archivo.
  • Utiliza JsonDocumentOptions para especificar que se permiten los comentarios en el JSON de entrada, pero se omiten.
  • Cuando termina, llama a Flush en el escritor. Una alternativa consiste en permitir el vaciado automático del escritor cuando se elimina.

Aquí se muestra un ejemplo de entrada JSON que el código de ejemplo va a procesar:

{"Class Name": "Science","Teacher's Name": "Jane","Semester": "2019-01-01","Students": [{"Name": "John","Grade": 94.3},{"Name": "James","Grade": 81.0},{"Name": "Julia","Grade": 91.9},{"Name": "Jessica","Grade": 72.4},{"Name": "Johnathan"}],"Final": true}

El resultado es la siguiente salida JSON impresa correctamente:

{
  "Class Name": "Science",
  "Teacher\u0027s Name": "Jane",
  "Semester": "2019-01-01",
  "Students": [
    {
      "Name": "John",
      "Grade": 94.3
    },
    {
      "Name": "James",
      "Grade": 81.0
    },
    {
      "Name": "Julia",
      "Grade": 91.9
    },
    {
      "Name": "Jessica",
      "Grade": 72.4
    },
    {
      "Name": "Johnathan"
    }
  ],
  "Final": true
}

JsonDocument es IDisposable

JsonDocument compila una vista en memoria de los datos en un búfer agrupado. Por lo tanto, el tipo JsonDocument implementa IDisposable y debe usarse dentro de un bloque using.

Devuelva un JsonDocument desde la API solo si quiere transferir la propiedad de la duración y derivar la responsabilidad al autor de la llamada. En la mayoría de los escenarios, eso no es necesario. Si el autor de la llamada necesita trabajar con todo el documento JSON, devuelva el Clone del RootElement, que es un JsonElement. Si el autor de la llamada necesita trabajar con un elemento determinado dentro del documento JSON, devuelva el Clone de dicho JsonElement. Si devuelve el RootElement o un subelemento directamente sin realizar un Clone, el autor de la llamada no podrá acceder al JsonElement devuelto después de que se elimine el JsonDocument que lo posee.

Este es un ejemplo en el que se le requiere que realice un Clone:

public JsonElement LookAndLoad(JsonElement source)
{
    string json = File.ReadAllText(source.GetProperty("fileName").GetString());

    using (JsonDocument doc = JsonDocument.Parse(json))
    {
        return doc.RootElement.Clone();
    }
}

El código anterior espera un JsonElement que contiene una propiedad fileName. Abre el archivo JSON y crea un JsonDocument. El método supone que el autor de la llamada quiere trabajar con todo el documento, por lo que devuelve el Clone del RootElement.

Si recibe un JsonElement y está devolviendo un subelemento, no es necesario devolver un Clone del subelemento. El autor de la llamada es responsable de mantener activo el JsonDocument al que pertenece el JsonElement pasado. Por ejemplo:

public JsonElement ReturnFileName(JsonElement source)
{
   return source.GetProperty("fileName");
}

JsonDocument con JsonSerializerOptions

Puede usar JsonSerializer para serializar y deserializar una instancia de JsonDocument. Pero la implementación para leer y escribir instancias JsonDocument mediante JsonSerializer es un contenedor en torno a JsonDocument.ParseValue(Utf8JsonReader) y JsonDocument.WriteTo(Utf8JsonWriter). Este contenedor no reenvía JsonSerializerOptions (características de serializador) a Utf8JsonReader o Utf8JsonWriter. Por ejemplo, si establece JsonSerializerOptions.DefaultIgnoreCondition en WhenWritingNull y llama a JsonSerializer con una sobrecarga que toma JsonSerializerOptions, no se omiten las propiedades null.

En el ejemplo siguiente se muestra el resultado del uso de métodos que toman un parámetro JsonSerializerOptions y serializan una instancia JsonDocument:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonDocumentWithJsonSerializerOptions;

public class Program
{
    public static void Main()
    {
        Person person = new() { Name = "Nancy" };

        // Default serialization - Address property included with null token.
        // Output: {"Name":"Nancy","Address":null}
        string personJsonWithNull = JsonSerializer.Serialize(person);
        Console.WriteLine(personJsonWithNull);

        // Serialize and ignore null properties - null Address property is omitted
        // Output: {"Name":"Nancy"}
        JsonSerializerOptions options = new()
        {
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
        };
        string personJsonWithoutNull = JsonSerializer.Serialize(person, options);
        Console.WriteLine(personJsonWithoutNull);

        // Ignore null properties doesn't work when serializing JsonDocument instance
        // by using JsonSerializer.
        // Output: {"Name":"Nancy","Address":null}
        JsonDocument? personJsonDocument = JsonSerializer.Deserialize<JsonDocument>(personJsonWithNull);
        personJsonWithNull = JsonSerializer.Serialize(personJsonDocument, options);
        Console.WriteLine(personJsonWithNull);
    }
}
public class Person
{
    public string? Name { get; set; }
    public string? Address { get; set; }
}

Si necesita características de JsonSerializerOptions, use JsonSerializer con destinos fuertemente tipados (como la clase Person de este ejemplo) en lugar de JsonDocument.

Vea también