Lenguaje
Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se muestra cómo manejar 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.
Gestionar el desbordamiento de JSON
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, como estas propiedades, aplique el atributo [JsonExtensionData] a una propiedad o campo. Use una de estas declaraciones compatibles:
- Declare el miembro de datos de extensión como JsonObject.
- Declare el miembro extension-data como
IDictionary<string, object>. - Declare el miembro extension-data como
IDictionary<string, JsonElement>. - Declare el miembro extension-data como
IReadOnlyDictionary<string, object>en .NET 11 y versiones posteriores. - Declare el miembro extension-data como
IReadOnlyDictionary<string, JsonElement>en .NET 11 y versiones posteriores.
Para los datos de extensión de un diccionario modificable, use cualquier tipo asignable a una de las interfaces admitidas IDictionary, como Dictionary<string, JsonElement>. Las declaraciones de solo lectura deben usar uno de los tipos de interfaz exactos IReadOnlyDictionary enumerados anteriormente.
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
Para un miembro de datos de extensión IReadOnlyDictionary, .NET 11 materializa un Dictionary<string, object> o Dictionary<string, JsonElement>. El miembro debe ser escribible porque el serializador inicializa el nuevo diccionario a partir de los valores existentes y vuelve a asignarlo. Si una propiedad JSON entrante duplica una clave existente, el valor entrante gana.
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.
A partir de .NET 11, la serialización aplana los datos de extensión JsonObject en el objeto JSON que los contiene. Las propiedades JsonObject aparecen directamente en el objeto contenedor, y no bajo el nombre del miembro extension-data.
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 quieres tener flexibilidad respecto a qué JSON se acepta para una propiedad concreta, una alternativa es deserializar 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"
// ]
//}