Come usare un modello DOM (Document Object Model) JSON in System.Text.Json

Questo articolo illustra come usare un modello DOM (Document Object Model) JSON per l'accesso casuale ai dati in un payload JSON.

Scelte per il modello DOM JSON

L'uso di un modello DOM è un'alternativa alla deserializzazione con JsonSerializer quando:

  • Non è disponibile un tipo di destinazione della deserializzazione.
  • Il codice JSON ricevuto non ha uno schema fisso e deve essere controllato per sapere cosa contiene.

System.Text.Json offre due modi per creare un modello DOM JSON:

  • JsonDocument consente di creare un modello DOM di sola lettura tramite Utf8JsonReader. È possibile accedere agli elementi JSON che compongono il payload tramite il tipo JsonElement. Il tipoJsonElement fornisce gli enumeratori di matrice e oggetto JSON insieme alle API per convertire il testo JSON in tipi .NET comuni. JsonDocument espone una proprietà RootElement. Per ottenere ulteriori informazioni, consultare la sezione Utilizzare JsonDocument più avanti in questo articolo.

  • JsonNode e le classi derivate nello spazio dei nomi System.Text.Json.Nodes offrono la possibilità di creare un modello DOM modificabile. È possibile accedere agli elementi JSON che compongono il payload tramite i tipi JsonNode, JsonObject, JsonArray, JsonValue e JsonElement. Per ottenere ulteriori informazioni, consultare la sezione Usare JsonNode più avanti in questo articolo.

Per la scelta tra JsonDocument e JsonNode, tenere presenti i fattori seguenti:

  • Il modello DOM JsonNode può essere modificato dopo la creazione. Il modello DOM JsonDocument non è modificabile.
  • Il modello DOM JsonDocument consente un accesso più rapido ai dati.

Utilizzare JsonNode.

Nell'esempio seguente viene illustrato come usare JsonNode e gli altri tipi nello spazio dei nomi System.Text.Json.Nodes per:

  • Creare un modello DOM da una stringa JSON
  • Scrivere un codice JSON da un modello DOM.
  • Ottenere un valore, un oggetto o una matrice da un modello 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"
    }
}

Creare un modello DOM JsonNode con inizializzatori di oggetti e apportare modifiche

L'esempio seguente illustra come:

  • Creare un modello DOM usando gli inizializzatori di oggetti.
  • Apportare modifiche a un modello 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
        //    }
        //  }
        //}
    }
}

Deserializzare le sottosezioni di un payload JSON

L'esempio seguente illustra come usare JsonNode per passare a una sottosezione di un albero JSON e deserializzare un singolo valore, un tipo personalizzato o una matrice da tale sottosezione.

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

Esempio di voto medio JsonNode

Nell'esempio seguente viene selezionata una matrice JSON con valori interi e viene calcolato un valore 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)!;

        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

Il codice precedente:

  • Calcola un voto medio per gli oggetti in una matrice Students con una proprietà Grade.
  • Assegna un voto predefinito pari a 70 per gli studenti che non hanno un voto.
  • Ottiene il numero di studenti dalla proprietà Count di JsonArray.

JsonNode con JsonSerializerOptions

È possibile usare JsonSerializer per serializzare e deserializzare un'istanza di JsonNode. Tuttavia, se si usa un overload che accetta JsonSerializerOptions, l'istanza delle opzioni viene usata solo per ottenere convertitori personalizzati. Le altre funzionalità dell'istanza delle opzioni non vengono usate. Ad esempio, se si imposta JsonSerializerOptions.DefaultIgnoreCondition su WhenWritingNull e si chiama JsonSerializer con un overload che accetta JsonSerializerOptions, le proprietà Null non verranno ignorate.

La stessa limitazione si applica ai metodi JsonNode che accettano un parametro JsonSerializerOptions: WriteTo(Utf8JsonWriter, JsonSerializerOptions) e ToJsonString(JsonSerializerOptions). Queste API usano JsonSerializerOptions solo per ottenere convertitori personalizzati.

Nell'esempio seguente viene illustrato il risultato dell'utilizzo di metodi che accettano un parametro JsonSerializerOptions e serializzano un'istanza di 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; }
}

Se sono necessarie funzionalità JsonSerializerOptions diverse dai convertitori personalizzati, usare JsonSerializer con destinazioni fortemente tipizzate (come la classe Person in questo esempio), anziché JsonNode.

Modificare l'ordine delle proprietà

JsonObject è uno degli elementi nel payload di un JsonNodeoggetto e rappresenta un oggetto JSON modificabile. Anche se il tipo è modellato come IDictionary<string, JsonNode>, dove ogni voce è una proprietà dell'oggetto, incapsula un ordine di proprietà implicito. Tuttavia, le API come Insert(Int32, String, JsonNode) e RemoveAt(Int32) modellano efficacemente il tipo come dizionario ordinato consentendo di inserire e rimuovere elementi in un indice specifico. Queste API consentono modifiche alle istanze di oggetti che possono influenzare direttamente l'ordine delle proprietà.

Il codice seguente mostra un esempio di aggiunta o spostamento di una proprietà specifica all'inizio dell'oggetto.

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

Confrontare JsonNodes

Per confrontare due JsonNode oggetti per verificarne l'uguaglianza, inclusi i relativi elementi discendenti, usare il JsonNode.DeepEquals(JsonNode, JsonNode) metodo .

Utilizzare JsonDocument.

L'esempio seguente illustra come usare la classe JsonDocument per l'accesso casuale ai dati in una stringa 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}")

Il codice precedente:

  • Si supponga che il codice JSON da analizzare si trovi in una stringa denominata jsonString.
  • Calcola un voto medio per gli oggetti in una matrice Students con una proprietà Grade.
  • Assegna un voto predefinito pari a 70 per gli studenti che non hanno un voto.
  • Crea l'istanza di JsonDocument in un'istruzione using perché JsonDocument implementa IDisposable. Dopo l'eliminazione di un'istanza di JsonDocument, si perde anche l'accesso a tutte le relative istanze JsonElement. Per mantenere l'accesso a un'istanza di JsonElement, crearne una copia prima dell'eliminazione dell'istanza padre di JsonDocument. Per creare una copia, chiamare JsonElement.Clone. Per ottenere ulteriori informazioni, consultare la sezione JsonDocument è IDisposable.

Il codice di esempio precedente conta gli studenti incrementando una variabile count con ogni iterazione. Un'alternativa consiste nel chiamare GetArrayLength, come illustrato nell'esempio seguente:

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

Di seguito è riportato un esempio di JSON elaborato da questo codice:

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

Per un esempio simile che usa JsonNode anziché JsonDocument, consultare la sezione Esempio di voto medio JsonNode.

Come cercare sottoelementi in JsonDocument e JsonElement

Le ricerche in JsonElement richiedono una ricerca sequenziale delle proprietà e quindi sono relativamente lente (ad esempio quando si usa TryGetProperty). System.Text.Json è progettato per ridurre al minimo il tempo di analisi iniziale anziché il tempo di ricerca. Pertanto, usare gli approcci seguenti per ottimizzare le prestazioni durante la ricerca in un oggetto JsonDocument:

  • Usare gli enumeratori predefiniti (EnumerateArray e EnumerateObject) anziché eseguire cicli o indicizzazione personalizzati.
  • Non eseguire una ricerca sequenziale nell'intero JsonDocument per ogni proprietà usando RootElement. Cercare invece gli oggetti JSON annidati in base alla struttura nota dei dati JSON. Gli esempi di codice precedenti, ad esempio, cercano una proprietà Grade negli oggetti Student eseguendo un ciclo negli oggetti Student e recuperando il valore di Grade per ognuno, anziché eseguire ricerche in tutti gli oggetti JsonElement alla ricerca delle proprietà Grade. Quest'ultima operazione comporterebbe passaggi non necessari sugli stessi dati.

Confrontare JsonElements

Per confrontare due JsonElement oggetti per verificarne l'uguaglianza, inclusi i relativi elementi discendenti, usare il JsonElement.DeepEquals(JsonElement, JsonElement) metodo .

JsonElement left = JsonDocument.Parse("10e-3").RootElement;
JsonElement right = JsonDocument.Parse("0.01").RootElement;
bool equal = JsonElement.DeepEquals(left, right);
Console.WriteLine(equal); // True.

Usare JsonDocument per scrivere JSON

L'esempio seguente illustra come scrivere JSON da 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()

Il codice precedente:

  • Legge un file JSON, carica i dati in un JsonDocument e scrive codice JSON formattato (pretty-print) in un file.
  • Usa JsonDocumentOptions per specificare che i commenti nel codice JSON di input sono consentiti ma vengono ignorati.
  • Al termine, chiama Flush per il writer. Un'alternativa consiste nel consentire lo scaricamento automatico del writer quando viene eliminato.

Di seguito è riportato un esempio di input JSON da elaborare con il codice di esempio:

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

Il risultato è il seguente output JSON formattato:

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

JsonDocument crea una vista in memoria dei dati in un buffer in pool. Di conseguenza, il tipo JsonDocument implementa IDisposable e deve essere usato all'interno di un blocco using.

Restituire un JsonDocument dall'API solo se si vuole trasferire la proprietà della durata e la responsabilità dell'eliminazione al chiamante. Nella maggior parte degli scenari, ciò non è necessario. Se il chiamante deve lavorare con l'intero documento JSON, restituire il Clone di RootElement, ovvero JsonElement. Se il chiamante deve lavorare con un particolare elemento all'interno del documento JSON, restituire il Clone di tale JsonElement. Se si restituisce RootElement o un sottoelemento direttamente senza creare un Clone, il chiamante non sarà in grado di accedere al JsonElement restituito dopo che il JsonDocument che lo possiede è stato eliminato.

Di seguito è riportato un esempio che richiede di creare 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();
    }
}

Il codice precedente si aspetta un JsonElement che contiene una proprietà fileName. Apre il file JSON e crea un JsonDocument. Il metodo presuppone che il chiamante voglia lavorare con l'intero documento, quindi restituisce il Clone di RootElement.

Se si riceve un JsonElement e si restituisce un sottoelemento, non è necessario restituire un Clone del sottoelemento. Il chiamante è responsabile di mantenere vivo il JsonDocument a cui appartiene il JsonElement passato. Ad esempio:

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

JsonDocument con JsonSerializerOptions

È possibile usare JsonSerializer per serializzare e deserializzare un'istanza di JsonDocument. Tuttavia, l'implementazione per la lettura e la scrittura di istanze di JsonDocument tramite JsonSerializer è un wrapper su JsonDocument.ParseValue(Utf8JsonReader) e JsonDocument.WriteTo(Utf8JsonWriter). Questo wrapper non inoltra alcun JsonSerializerOptions (funzionalità del serializzatore) a Utf8JsonReader o Utf8JsonWriter. Ad esempio, se si imposta JsonSerializerOptions.DefaultIgnoreCondition su WhenWritingNull e si chiama JsonSerializer con un overload che accetta JsonSerializerOptions, le proprietà Null non verranno ignorate.

Nell'esempio seguente viene illustrato il risultato dell'utilizzo di metodi che accettano un parametro JsonSerializerOptions e serializzano un'istanza di 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; }
}

Se sono necessarie funzionalità JsonSerializerOptions, usare JsonSerializer con destinazioni fortemente tipizzate (come la classe Person in questo esempio), anziché JsonDocument.

Vedi anche