Comment gérer le dépassement JSON ou utiliser JsonElement ou JsonNode dans System.Text.Json

Cet article explique comment gérer le dépassement JSON avec l’espace de System.Text.Json noms. Il montre également comment désérialiser dans JsonElement ou JsonNode, en tant qu’alternative pour d’autres scénarios où le type cible peut ne pas correspondre parfaitement à tout le JSON en cours de désérialisation.

Gérer le JSON de dépassement

Lors de la désérialisation, vous pouvez recevoir des données dans le JSON qui ne sont pas représentées par les propriétés du type cible. Par exemple, supposons que votre type cible est le suivant :

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

Et que le JSON à désérialiser est le suivant :

{
  "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 vous désérialisez le JSON affiché dans le type affiché, les propriétés DatesAvailable et SummaryWords n’ont nulle part où aller et sont perdues. Pour capturer des données supplémentaires comme ces propriétés, appliquez l’attribut [JsonExtensionData] à une propriété de type Dictionary<string,object> ou 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

Le tableau suivant montre le résultat de la désérialisation du JSON indiqué précédemment dans cet exemple de type. Les données supplémentaires deviennent des paires clé-valeur de la propriété ExtensionData :

Propriété Valeur Notes
Date "8/1/2019 12:00:00 AM -07:00"
TemperatureCelsius 0 Non-correspondance de casse (temperatureCelsius dans le JSON), la propriété n’est donc pas définie.
Summary "Hot"
ExtensionData "temperatureCelsius": 25,
"DatesAvailable": ["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],
"SummaryWords": ["Cool","Windy","Humid"]
Étant donné que la casse ne correspond pas, temperatureCelsius est un supplément et devient une paire clé-valeur dans le dictionnaire.
Chaque tableau supplémentaire du JSON devient une paire clé-valeur, avec un tableau comme objet valeur.

Lorsque l’objet cible est sérialisé, les paires de valeurs de clé de données d’extension deviennent des propriétés JSON comme elles l’étaient dans le JSON entrant :

{
  "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"
  ]
}

Notez que le nom de la propriété ExtensionData n’apparaît pas dans le JSON. Ce comportement permet au JSON d’effectuer un aller-retour sans perdre de données supplémentaires qui, sinon, ne seraient pas désérialisées.

L’exemple suivant montre un aller-retour de JSON vers un objet désérialisé, puis à nouveau vers 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"
//  ]
//}

Désérialiser dans JsonElement ou JsonNode

Si vous souhaitez simplement être flexible quant à ce que le JSON accepte pour une propriété particulière, une alternative consiste à désérialiser dans JsonElement ou JsonNode. Toute propriété JSON valide peut être désérialisée dans JsonElement ou JsonNode. Choisissez JsonElement pour créer un objet immuable ou JsonNode pour créer un objet mutable.

L’exemple suivant montre un aller-retour de JSON et de retour vers JSON pour une classe qui inclut des propriétés de type JsonElement et 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"
//  ]
//}

Voir aussi