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 tipoJsonElement
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 DOMJsonDocument
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)!;
JsonArray studentsArray = document["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 propiedadGrade
. - 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
deJsonArray
.
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
.
Manipular el orden de las propiedades
JsonObject es uno de los elementos de la carga de JsonNode y representa un objeto JSON mutable. Aunque el tipo se modela como un IDictionary<string, JsonNode>
, donde cada entrada es una propiedad del objeto, encapsula un orden de propiedades implícito. Sin embargo, APIs como Insert(Int32, String, JsonNode) y RemoveAt(Int32) modelan efectivamente el tipo como un diccionario ordenado al permitirte insertar y eliminar elementos en un índice específico. Estas API permiten modificaciones en las instancias del objeto que pueden influir directamente en el orden de las propiedades.
El siguiente código muestra un ejemplo de añadir o mover una propiedad específica al inicio del objeto.
var schema = (JsonObject)JsonSerializerOptions.Default.GetJsonSchemaAsNode(typeof(MyPoco));
JsonNode? idValue;
switch (schema.IndexOf("$id"))
{
// $id property missing.
case < 0:
idValue = (JsonNode)"https://example.com/schema";
schema.Insert(0, "$id", idValue);
break;
// $id property already at the start of the object.
case 0:
break;
// $id exists but not at the start of the object.
case int index:
idValue = schema[index];
schema.RemoveAt(index);
schema.Insert(0, "$id", idValue);
break;
}
Comparar JsonNodes
Para comparar dos objetos JsonNode
por igualdad, incluyendo sus elementos descendientes, use el método JsonNode.DeepEquals(JsonNode, 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 propiedadGrade
. - Asigna una calificación predeterminada de 70 a los alumnos que no tienen ninguna calificación.
- Crea la instancia
JsonDocument
enusing
(Instrucción) porqueJsonDocument
implementaIDisposable
. Después de eliminar una instanciaJsonDocument
, también se pierde el acceso a todas sus instanciasJsonElement
. Para conservar el acceso a una instanciaJsonElement
, realice una copia de ella antes de eliminar la instancia primariaJsonDocument
. 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 medianteRootElement
. 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 propiedadGrade
en objetosStudent
al recorrer en bucle los objetosStudent
y obtener el valor deGrade
de cada uno, en lugar de buscar en todos los objetosJsonElement
las propiedadesGrade
. Si se hace esto último, se pasaría innecesariamente por los mismos datos.
Comparar JsonElements
Para comparar dos objetos JsonElement
por igualdad, incluyendo sus elementos descendientes, use el método JsonElement.DeepEquals(JsonElement, JsonElement).
JsonElement left = JsonDocument.Parse("10e-3").RootElement;
JsonElement right = JsonDocument.Parse("0.01").RootElement;
bool equal = JsonElement.DeepEquals(left, right);
Console.WriteLine(equal); // True.
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
.