Guide pratique pour utiliser un modèle objet de document JSON dans System.Text.Json

Cet article montre comment utiliser un modèle DOM (Document Object Model) JSON pour l’accès aléatoire aux données dans une charge utile JSON.

Choix de DOM JSON

L’utilisation d’un DOM est une alternative à la désérialisation avec JsonSerializer :

  • Lorsque vous n’avez pas de type dans lequel désérialiser.
  • Lorsque le JSON que vous recevez n’a pas de schéma fixe et doit être inspecté pour savoir ce qu’il contient.

System.Text.Json fournit deux façons de générer un DOM JSON :

  • JsonDocument permet de créer un DOM en lecture seule à l’aide de Utf8JsonReader. Les éléments JSON qui composent la charge utile sont accessibles via le type JsonElement. Le type JsonElement fournit le tableau et les énumérateurs d’objets JSON, ainsi que des API pour convertir le texte JSON en types .NET courants. JsonDocument expose une propriété RootElement. Pour plus d’informations, consultez Utilisation de JsonDocument, plus loin dans cet article.

  • JsonNode et les classes qui en dérivent dans l’espace de noms System.Text.Json.Nodes permettent de créer un DOM mutable. Les éléments JSON qui composent la charge utile sont accessibles via les types JsonNode, JsonObject, JsonArray, JsonValue et JsonElement. Pour plus d’informations, consultez Utiliser JsonNode plus loin dans cet article.

Tenez compte des facteurs suivants lors du choix entre JsonDocument et JsonNode :

  • Le DOM JsonNode peut être modifié après sa création. Le DOM JsonDocument est immuable.
  • Le DOM JsonDocument offre un accès plus rapide à ses données.

Utilisez JsonNode.

L’exemple suivant montre comment utiliser JsonNode et les autres types de l’espace de noms System.Text.Json.Nodes pour :

  • Créer un DOM à partir d’une chaîne JSON
  • Écrire du JSON à partir d’un DOM.
  • Obtenir une valeur, un objet ou un tableau à partir d’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"
    }
}

Créer un DOM JsonNode avec des initialiseurs d’objet et apporter des modifications

L’exemple suivant montre comment :

  • Créer un DOM à l’aide d’initialiseurs d’objets.
  • Apporter des modifications à 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
        //    }
        //  }
        //}
    }
}

Désérialiser les sous-sections d’une charge utile JSON

L’exemple suivant montre comment utiliser JsonNode pour accéder à une sous-section d’une arborescence JSON et désérialiser une valeur unique, un type personnalisé ou un tableau de cette sous-section.

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
    }
}

Exemple de note moyenne avec JsonNode

L’exemple suivant sélectionne un tableau JSON qui contient des valeurs entières et calcule une valeur moyenne :

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

Le code précédent :

  • Calcule une note moyenne pour les objets d’un tableau Students qui ont une propriété Grade.
  • Attribue une note par défaut de 70 aux étudiants qui n’ont pas de note.
  • Obtient le nombre d’étudiants à partir de la propriété Count de JsonArray.

JsonNode avec JsonSerializerOptions

Vous pouvez utiliser JsonSerializer pour sérialiser et désérialiser une instance de JsonNode. Toutefois, si vous utilisez une surcharge qui prend JsonSerializerOptions, l’instance options est utilisée uniquement pour obtenir des convertisseurs personnalisés. Les autres fonctionnalités de l’instance options ne sont pas utilisées. Par exemple, si vous définissez JsonSerializerOptions.DefaultIgnoreCondition sur WhenWritingNull et appelez JsonSerializer avec une surcharge qui prend JsonSerializerOptions, les propriétés null ne seront pas ignorées.

La même limitation s’applique aux méthodes JsonNode qui prennent un paramètre JsonSerializerOptions : WriteTo(Utf8JsonWriter, JsonSerializerOptions) et ToJsonString(JsonSerializerOptions). Ces API utilisent JsonSerializerOptions uniquement pour obtenir des convertisseurs personnalisés.

L’exemple suivant illustre le résultat de l’utilisation de méthodes qui prennent un paramètre JsonSerializerOptions et sérialisent une instance 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 vous avez besoin de fonctionnalités JsonSerializerOptions autres que des convertisseurs personnalisés, utilisez JsonSerializer avec des cibles fortement typées (comme la classe Person dans cet exemple) plutôt que JsonNode.

Manipuler l’ordre des propriétés

JsonObject est l’un des éléments dans la charge utile d’un JsonNode, et il représente un objet JSON mutable. Même si le type est modélisé comme un IDictionary<string, JsonNode>, où chaque entrée est une propriété de l’objet, il encapsule un ordre implicite des propriétés. Cependant, les API telles que Insert(Int32, String, JsonNode) et RemoveAt(Int32) modélisent effectivement le type comme un dictionnaire ordonné en vous permettant d’insérer et de supprimer des éléments à un indice spécifique. Ces API permettent des modifications des instances d’objet qui peuvent directement influencer l’ordre des propriétés.

Le code suivant montre un exemple d’ajout ou de déplacement d’une propriété spécifique au début de l’objet.

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;
}

Comparer des JsonNodes

Pour comparer deux objets JsonNode pour l’égalité, y compris leurs éléments descendants, utilisez la méthode JsonNode.DeepEquals(JsonNode, JsonNode).

Utilisez JsonDocument.

L’exemple suivant montre comment utiliser la classe JsonDocument pour l’accès aléatoire aux données dans une chaîne 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}")

Le code précédent :

  • Suppose que le JSON à analyser se trouve dans une chaîne nommée jsonString.
  • Calcule une note moyenne pour les objets d’un tableau Students qui ont une propriété Grade.
  • Attribue une note par défaut de 70 aux étudiants qui n’ont pas de note.
  • Crée l’instance JsonDocument dans une instruction using, car JsonDocument implémente IDisposable. Une fois qu’une instance JsonDocument est supprimée, vous perdez également l’accès à toutes ses instances JsonElement. Pour conserver l’accès à une instance JsonElement, effectuez une copie avant la suppression de l’instance parente JsonDocument. Pour effectuer une copie, appelez JsonElement.Clone. Pour plus d’informations, consultez JsonDocument est IDisposable.

L’exemple de code précédent compte les étudiants en incrémentant une variable count à chaque itération. Une alternative consiste à appeler GetArrayLength, comme illustré dans l’exemple suivant :

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}")

Voici un exemple du JSON que ce code traite :

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

Pour obtenir un exemple similaire qui utilise JsonNode au lieu de JsonDocument, consultez Exemple de note moyenne avec JsonNode.

Comment rechercher des sous-éléments dans JsonDocument et JsonElement

Les recherches sur JsonElement nécessitent une recherche séquentielle des propriétés et sont donc relativement lentes (par exemple lors de l’utilisation de TryGetProperty). System.Text.Json est conçu pour réduire le temps d’analyse initial plutôt que le temps de recherche. Par conséquent, utilisez les approches suivantes pour optimiser les performances lors de la recherche dans un objet JsonDocument :

  • Utilisez les énumérateurs intégrés (EnumerateArray et EnumerateObject) plutôt que d’effectuer vos propres boucles ou indexations.
  • N’effectuez pas de recherche séquentielle dans l’ensemble de JsonDocument via chaque propriété en utilisant RootElement. Au lieu de cela, recherchez des objets JSON imbriqués en fonction de la structure connue des données JSON. Par exemple, les exemples de code précédents recherchent une propriété Grade dans les objets Student en faisant une boucle dans les objets Student et en obtenant la valeur de Grade pour chacun, plutôt que de rechercher tous les objets JsonElement à la recherche de propriétés Grade. L’exécution de cette dernière approche entraînerait des passages inutiles sur les mêmes données.

Comparer des JsonElements

Pour comparer deux objets JsonElement pour l’égalité, y compris leurs éléments descendants, utilisez la méthode 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.

Utiliser JsonDocument pour écrire du JSON

L’exemple suivant montre comment écrire du code JSON à partir d’un 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()

Le code précédent :

  • Lit un fichier JSON, charge les données dans un fichier JsonDocument et écrit un fichier JSON mis en forme (imprimé élégamment).
  • Utilise JsonDocumentOptions pour spécifier que les commentaires dans le JSON d’entrée sont autorisés mais ignorés.
  • Une fois cela terminé, appelle Flush sur l’enregistreur. Une alternative consiste à laisser l’enregistreur se vider automatiquement lorsqu’il est supprimé.

Voici un exemple d’entrée JSON à traiter par l’exemple de code :

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

Le résultat est la sortie JSON imprimée de façon élégante suivante :

{
  "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 est IDisposable

JsonDocument génère une vue en mémoire des données dans une mémoire tampon mise en pool. Par conséquent, le type JsonDocument implémente IDisposable et doit être utilisé à l’intérieur d’un bloc using.

Retournez un JsonDocument à partir de votre API uniquement si vous souhaitez transférer la propriété de la durée de vie et éliminer la responsabilité de l’appelant. Dans la plupart des scénarios, cela n’est pas nécessaire. Si l’appelant doit travailler avec l’intégralité du document JSON, retournez le Clone du RootElement, qui est un JsonElement. Si l’appelant doit travailler avec un élément particulier dans le document JSON, retournez le Clone de ce JsonElement. Si vous retournez le RootElement ou un sous-élément directement sans effectuer de Clone, l’appelant ne pourra pas accéder au JsonElement retourné une fois que le JsonDocument propriétaire est supprimé.

Voici un exemple qui vous oblige à effectuer 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();
    }
}

Le code précédent attend un JsonElement qui contient une propriété fileName. Il ouvre le fichier JSON et crée un JsonDocument. La méthode part du principe que l’appelant souhaite travailler avec l’ensemble du document. Elle retourne donc le Clone du RootElement.

Si vous recevez un JsonElement et que vous retournez un sous-élément, il n’est pas nécessaire de retourner un Clone de ce sous-élément. L’appelant est chargé de maintenir en vie le JsonDocument auquel appartient le JsonElement passé. Par exemple :

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

JsonDocument avec JsonSerializerOptions

Vous pouvez utiliser JsonSerializer pour sérialiser et désérialiser une instance de JsonDocument. Toutefois, l’implémentation de lecture et d’écriture d’instances JsonDocument à l’aide de JsonSerializer est un wrapper sur JsonDocument.ParseValue(Utf8JsonReader) et JsonDocument.WriteTo(Utf8JsonWriter). Ce wrapper ne transfère pas de JsonSerializerOptions (fonctionnalités de sérialiseur) vers Utf8JsonReader ou Utf8JsonWriter. Par exemple, si vous définissez JsonSerializerOptions.DefaultIgnoreCondition sur WhenWritingNull et appelez JsonSerializer avec une surcharge qui prend JsonSerializerOptions, les propriétés null ne seront pas ignorées.

L’exemple suivant illustre le résultat de l’utilisation de méthodes qui prennent un paramètre JsonSerializerOptions et sérialisent une instance de 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 vous avez besoin de fonctionnalités JsonSerializerOptions, utilisez JsonSerializer avec des cibles fortement typées (comme la classe Person dans cet exemple) plutôt que JsonDocument.

Voir aussi