Cómo manejar el desbordamiento JSON o utilizar JsonElement o JsonNode
En este artículo, aprenderás a controlar el código JSON de desbordamiento con el espacio de nombres System.Text.Json. También muestra cómo deserializar en JsonElement o JsonNode, como alternativa a otros escenarios en los que el tipo de destino podría no coincidir perfectamente con todos los JSON que se deserializan.
JSON de desbordamiento de control
Durante la deserialización, es posible que reciba datos en el archivo JSON que no están representados por las propiedades del tipo de destino. Por ejemplo, supongamos que el tipo de destino es el siguiente:
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Public Class WeatherForecast
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
Y el JSON que se va a deserializar es el siguiente:
{
"Date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Si deserializa el JSON que se muestra en el tipo mostrado, las propiedades DatesAvailable
y SummaryWords
no tienen a donde ir y se pierden. Para capturar datos adicionales tales como estas propiedades, aplique el atributo [JsonExtensionData] a una propiedad de tipo Dictionary<string,object>
o Dictionary<string,JsonElement>
:
public class WeatherForecastWithExtensionData
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
Public Class WeatherForecastWithExtensionData
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
<JsonExtensionData>
Public Property ExtensionData As Dictionary(Of String, Object)
End Class
En la tabla siguiente se muestra el resultado de deserializar el JSON mostrado anteriormente en este tipo de ejemplo. Los datos adicionales se convierten en pares clave-valor de la propiedad ExtensionData
:
Propiedad | Valor | Notas |
---|---|---|
Date |
"8/1/2019 12:00:00 AM -07:00" |
|
TemperatureCelsius |
0 |
Error de coincidencia con distinción de mayúsculas y minúsculas (temperatureCelsius en el JSON), por lo que no se establece la propiedad. |
Summary |
"Hot" |
|
ExtensionData |
"temperatureCelsius": 25, "DatesAvailable": ["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"], "SummaryWords": ["Cool","Windy","Humid"] |
Dado que el caso no coincidía, temperatureCelsius es un extra y se convierte en un par clave-valor en el diccionario. La propiedad adicional del JSON se convierte en un par clave-valor, con una matriz como objeto de valor. |
Cuando se serializa el objeto de destino, los pares clave-valor de los datos de la extensión se convierten en propiedades JSON tal y como estaban en el JSON entrante:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 0,
"Summary": "Hot",
"temperatureCelsius": 25,
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Observe que el nombre de la propiedad ExtensionData
no aparece en el archivo JSON. Este comportamiento permite que JSON realice un viaje de ida y vuelta sin perder ningún dato adicional que de otro modo no se deserializaría.
En el ejemplo siguiente se muestra un recorrido de ida y vuelta de JSON a un objeto deserializado y de vuelta a JSON:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace RoundtripExtensionData
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""temperatureCelsius"": 25,
""Summary"": ""Hot"",
""SummaryField"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;
var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Console.WriteLine($"JSON output:\n{jsonString}\n");
}
}
}
// output:
//JSON output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 0,
// "Summary": "Hot",
// "temperatureCelsius": 25,
// "SummaryField": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}
Deserializar en JsonElement o JsonNode
Si solo quiere ser flexible sobre qué JSON aceptar para una propiedad determinada, una alternativa es deserializarlo en JsonElement o JsonNode. Cualquier propiedad JSON válida se puede deserializar en JsonElement
o JsonNode
. Elegir JsonElement
para crear un objeto inmutable o JsonNode
para crear un objeto mutable.
En el ejemplo siguiente se muestra un recorrido de ida y vuelta de JSON a JSON para una clase que incluye propiedades de tipo JsonElement
y JsonNode
.
using System.Text.Json;
using System.Text.Json.Nodes;
namespace RoundtripJsonElementAndNode
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public JsonElement DatesAvailable { get; set; }
public JsonNode? SummaryWords { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast? weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString);
var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}