Verwenden eines JSON-Dokumentobjektmodells in System.Text.Json

In diesem Artikel wird gezeigt, wie ein JSON-Dokumentobjektmodell (DOM) für den zufälligen Zugriff auf Daten in JSON-Nutzdaten verwendet wird.

JSON-DOM-Optionen

Das Arbeiten mit einem DOM stellt in den folgenden Fällen eine Alternative zur Deserialisierung mit JsonSerializer dar:

  • Sie verfügen über keinen Typ, in den Sie deserialisieren können.
  • Das empfangene JSON-Element verfügt über kein festes Schema und muss bezüglich des Inhalts überprüft werden.

System.Text.Json bietet zwei Möglichkeiten zum Erstellen eines JSON-DOM:

  • JsonDocument bietet die Möglichkeit, ein schreibgeschütztes DOM mithilfe von Utf8JsonReader zu erstellen. Auf die JSON-Elemente, aus denen sich die Nutzlast zusammensetzt, kann über den Typ JsonElement zugegriffen werden. Der JsonElement-Typ stellt Array- und Objektenumeratoren sowie APIs zum Konvertieren von JSON-Text in gängige .NET-Typen bereit. JsonDocument macht eine RootElement-Eigenschaft verfügbar. Weitere Informationen finden Sie unter Verwenden von „JsonDocument“ weiter unten in diesem Artikel.

  • JsonNode und die davon im Namespace System.Text.Json.Nodes abgeleiteten Klassen bieten die Möglichkeit, ein änderbares DOM zu erstellen. Auf die JSON-Elemente, aus denen sich die Nutzdaten zusammensetzen, kann über die Typen JsonNode, JsonObject, JsonArray, JsonValue und JsonElement zugegriffen werden. Weitere Informationen finden Sie unter Verwenden von JsonNode weiter unten in diesem Artikel.

Berücksichtigen Sie bei der Wahl zwischen JsonDocument und JsonNode die folgenden Faktoren:

  • Das Dokumentobjektmodell JsonNode kann nach dem Erstellen geändert werden. Das Dokumentobjektmodell JsonDocument ist unveränderlich.
  • Das Dokumentobjektmodell JsonDocument bietet schnelleren Zugriff auf seine Daten.

Verwenden Sie JsonNode

Das folgende Beispiel zeigt, wie Sie JsonNode und die anderen Typen im Namespace System.Text.Json.Nodes für folgende Schritte verwenden:

  • Erstellen eines DOM aus einer JSON-Zeichenfolge
  • Schreiben von JSON aus einem DOM
  • Abrufen von Werten, Objekten oder Arrays aus einem 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"
    }
}

Erstellen eines JsonNode-DOM mit Objektinitialisierern und Vornehmen von Änderungen

Das folgende Beispiel veranschaulicht die Vorgehensweise:

  • Erstellen eines DOM mithilfe von Objektinitialisierern
  • Vornehmen von Änderungen an einem 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
        //    }
        //  }
        //}
    }
}

Deserialisieren von Unterabschnitten von JSON-Nutzdaten

Im folgenden Beispiel wird gezeigt, wie Sie mit JsonNode zu einem Unterabschnitt einer JSON-Struktur navigieren und einen einzelnen Wert, einen benutzerdefinierten Typ oder ein Array aus diesem Unterabschnitt deserialisieren.

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

JsonNode-Durchschnittsnotenbeispiel

Im folgenden Beispiel wird ein JSON-Array mit ganzzahligen Werten ausgewählt und ein Durchschnittswert berechnet:

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

Der vorangehende Code:

  • Berechnet eine durchschnittliche Note für Objekte in einem Students-Array, die eine Grade-Eigenschaft besitzen.
  • Weist Kursteilnehmern, die keine Note haben, eine Standardnote von 70 zu.
  • Ruft die Anzahl der Kursteilnehmer*innen aus der Count-Eigenschaft von JsonArray ab.

JsonNode mit JsonSerializerOptions

Sie können JsonSerializer zum Serialisieren und Deserialisieren einer Instanz von JsonNode verwenden. Wenn Sie jedoch eine Überladung nutzen, die JsonSerializerOptions verwendet, wird die Optionsinstanz nur zum Abrufen von benutzerdefinierten Konvertern verwendet. Andere Features der Optionsinstanz werden nicht verwendet. Wenn Sie beispielsweise JsonSerializerOptions.DefaultIgnoreCondition auf WhenWritingNull festlegen und JsonSerializer mit einer Überladung aufrufen, die JsonSerializerOptions verwendet, werden NULL-Eigenschaften nicht ignoriert.

Die gleiche Einschränkung gilt für die JsonNode-Methoden, die einen JsonSerializerOptions-Parameter verwenden: WriteTo(Utf8JsonWriter, JsonSerializerOptions) und ToJsonString(JsonSerializerOptions). Diese APIs verwenden JsonSerializerOptions nur zum Abrufen von benutzerdefinierten Konvertern.

Das folgende Beispiel veranschaulicht das Ergebnis der Verwendung von Methoden, die einen JsonSerializerOptions-Parameter verwenden und eine JsonNode-Instanz serialisieren:

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

Wenn Sie abgesehen von benutzerdefinierten Konvertern andere Features von JsonSerializerOptions benötigen, verwenden Sie JsonSerializer mit stark typisierten Zielen (Person-Klasse in diesem Beispiel) anstelle von JsonNode.

Eigenschaftsreihenfolge verändern

JsonObject ist eines der Elemente im Payload eines JsonNode und stellt ein veränderbares JSON-Objekt dar. Auch wenn der Typ als IDictionary<string, JsonNode> modelliert ist, bei dem jeder Eintrag eine Eigenschaft des Objekts ist, kapselt er eine implizite Eigenschaftsreihenfolge ein. APIs wie z. B. Insert(Int32, String, JsonNode) und RemoveAt(Int32) modellieren den Typ jedoch effektiv als sortiertes Wörterbuch, indem Sie Elemente an einem bestimmten Index einfügen und entfernen können. Diese APIs bieten die Möglichkeit, Änderungen an den Instanzen eines Objekts vorzunehmen, die die Reihenfolge der Eigenschaften direkt beeinflussen können.

Der folgende Code zeigt ein Beispiel für das Hinzufügen oder Verschieben einer bestimmten Eigenschaft an den Anfang des Objekts.

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

JsonNodes vergleichen

Verwenden Sie die Methode JsonNode.DeepEquals(JsonNode, JsonNode), um zwei JsonNode-Objekte auf Gleichheit zu vergleichen, einschließlich der von ihnen abstammenden Elemente.

Verwenden Sie JsonDocument

Das folgende Beispiel zeigt, wie Sie die JsonDocument-Klasse für den zufälligen Zugriff auf Daten in einer JSON-Zeichenfolge verwenden:

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

Der vorangehende Code:

  • Setzt voraus, dass sich der zu analysierende JSON-Code in einer Zeichenfolge namens jsonString befindet.
  • Berechnet eine durchschnittliche Note für Objekte in einem Students-Array, die eine Grade-Eigenschaft besitzen.
  • Weist Kursteilnehmern, die keine Note haben, eine Standardnote von 70 zu.
  • Erstellt die JsonDocument-Instanz in einer using-Anweisung, da JsonDocumentIDisposable implementiert. Nachdem eine JsonDocument-Instanz verworfen wurde, verlieren Sie auch den Zugriff auf alle JsonElement-Instanzen. Um den Zugriff auf eine JsonElement-Instanz beizubehalten, erstellen Sie eine Kopie davon, bevor die übergeordnete JsonDocument-Instanz verworfen wird. Rufen Sie zum Erstellen einer Kopie JsonElement.Clone auf. Weitere Informationen finden Sie unter „JsonDocument“ ist „IDisposable“.

Im obigen Beispielcode wird die Anzahl der Kursteilnehmer*innen durch Erhöhen einer count-Variable mit jeder Iteration berechnet. Eine Alternative besteht darin, GetArrayLength aufzurufen, wie im folgenden Beispiel gezeigt:

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

Im Folgenden finden Sie ein Beispiel für das JSON, das von diesem Code verarbeitet wird:

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

Ein ähnliches Beispiel, das JsonNode anstelle von JsonDocument verwendet, finden Sie unter JsonNode-Durchschnittsnotenbeispiel.

Durchsuchen eines „JSonDocument“ und „JsonElement“ nach Unterelementen

Suchen im JsonElement erfordern eine sequenzielle Suche der Eigenschaften, sodass sie deswegen relativ langsam sind (z. B. bei Verwendung von TryGetProperty). System.Text.Json ist darauf ausgelegt, die anfängliche Analysezeit zu minimieren anstatt die Nachschlagezeit. Verwenden Sie deshalb die folgenden Ansätze, um die Leistung beim Durchsuchen eines JsonDocument-Objekts zu optimieren:

  • Verwenden Sie die integrierten Enumeratoren (EnumerateArray und EnumerateObject), anstatt ihre eigene Indizierung oder eigene Schleifen zu verwenden.
  • Führen Sie keine sequenzielle Suche im gesamten JsonDocument durch alle Eigenschaften durch, indem Sie RootElement verwenden. Suchen Sie stattdessen in geschachtelten JSON-Objekten, basierend auf der bekannten Struktur der JSON-Daten. In den obigen Codebeispielen wird beispielsweise nach einer Grade-Eigenschaft in Student-Objekten gesucht, indem eine Schleife für die Student-Objekte ausgeführt und jeweils der Wert von Grade abgerufen wird, anstatt alle JsonElement-Objekte auf Grade-Eigenschaften zu durchsuchen. Die letztgenannte Vorgehensweise führt zu unnötigen Durchläufen sämtlicher Daten.

JsonElements vergleichen

Um zwei JsonElement-Objekte auf Gleichheit zu vergleichen, einschließlich der von ihnen abstammenden Elemente, verwenden Sie die JsonElement.DeepEquals(JsonElement, JsonElement)-Methode.

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

Verwenden von JsonDocument zum Schreiben von JSON

Das folgende Beispiel veranschaulicht, wie JSON aus einem JsonDocument geschrieben wird:

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()

Der vorangehende Code:

  • Liest eine JSON-Datei, lädt die Daten in ein JsonDocument und schreibt (übersichtlich) formatierten JSON-Code in eine Datei.
  • Verwendet JsonDocumentOptions, um anzugeben, dass Kommentare in der JSON-Eingabe zulässig sind, aber ignoriert werden.
  • Ruft nach Abschluss Flush für den Writer auf. Eine Alternative ist, den Writer automatisch zu leeren, wenn er entsorgt wird.

Im Folgenden finden Sie ein Beispiel für eine JSON-Eingabe, die von dem Beispielcode verarbeitet werden soll:

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

Das Ergebnis ist die folgende, übersichtlich formatierte JSON-Ausgabe:

{
  "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“ ist „IDisposable“

JsonDocument erstellt eine In-Memory-Ansicht der Daten in einem gepoolten Puffer. Daher implementiert der JsonDocument-Typ IDisposable und muss in einem using-Block verwendet werden.

Gibt nur ein JsonDocument von ihrer API zurück, wenn Sie den Besitz der Lebensdauer und die Verantwortung für die Entsorgung an den Aufrufer übertragen möchten. In den meisten Szenarien ist dies nicht erforderlich. Wenn der Aufrufer mit dem gesamten JSON-Dokument arbeiten muss, geben Sie den Clone des RootElement zurück, bei dem es sich um ein JsonElement handelt. Wenn der Aufrufer mit einem bestimmten Element innerhalb des JSON-Dokuments arbeiten muss, geben Sie den Clone dieses JsonElement zurück. Wenn Sie das RootElement oder ein Unterelement direkt zurückgeben, ohne einen Clone zu erstellen, kann der Aufrufer nicht auf das zurückgegebene JsonElement zugreifen, nachdem das JsonDocument, das dessen Besitzer ist, entsorgt wurde.

Hier sehen Sie ein Beispiel, in dem Sie einen Clone erstellen müssen:

public JsonElement LookAndLoad(JsonElement source)
{
    string json = File.ReadAllText(source.GetProperty("fileName").GetString());

    using (JsonDocument doc = JsonDocument.Parse(json))
    {
        return doc.RootElement.Clone();
    }
}

Der vorangehende Code erwartet ein JsonElement, das eine fileName-Eigenschaft enthält. Er öffnet die JSON-Datei und erstellt ein JsonDocument. Die Methode setzt voraus, dass der Aufrufer mit dem gesamten Dokument arbeiten möchte, weshalb sie den Clone des RootElement zurückgibt.

Wenn Sie ein JsonElement erhalten und ein Unterelement zurückgeben, ist es nicht notwendig, einen Clone des Unterelements zurückzugeben. Der Aufrufer ist dafür verantwortlich, das JsonDocument zu erhalten, zu dem das übergebene JsonElement gehört. Zum Beispiel:

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

JsonDocument mit JsonSerializerOptions

Sie können JsonSerializer zum Serialisieren und Deserialisieren einer Instanz von JsonDocument verwenden. Die Implementierung für das Lesen und Schreiben von JsonDocument-Instanzen mithilfe von JsonSerializer ist ein Wrapper für JsonDocument.ParseValue(Utf8JsonReader) und JsonDocument.WriteTo(Utf8JsonWriter). Dieser Wrapper leitet keine JsonSerializerOptions (Serialisierungsfeatures) an Utf8JsonReader oder Utf8JsonWriter weiter. Wenn Sie beispielsweise JsonSerializerOptions.DefaultIgnoreCondition auf WhenWritingNull festlegen und JsonSerializer mit einer Überladung aufrufen, die JsonSerializerOptions verwendet, werden NULL-Eigenschaften nicht ignoriert.

Das folgende Beispiel veranschaulicht das Ergebnis der Verwendung von Methoden, die einen JsonSerializerOptions-Parameter verwenden und eine JsonDocument-Instanz serialisieren:

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

Wenn Sie Features von JsonSerializerOptions benötigen, verwenden Sie JsonSerializer mit stark typisierten Zielen (Person-Klasse in diesem Beispiel) anstelle von JsonDocument.

Weitere Informationen