Поделиться через


Использование объектной модели документа JSON в System.Text.Json

В этой статье показано, как использовать объектную модель документа JSON (DOM) для случайного доступа к данным в полезных данных JSON.

Варианты JSON DOM

Работа с DOM является альтернативой десериализации с помощью JsonSerializer :

  • У вас нет типа для десериализации.
  • Полученный код JSON не имеет фиксированной схемы и должен быть проверен, чтобы узнать, что он содержит.

System.Text.Json предоставляет два способа создания DOM JSON:

  • JsonDocument предоставляет возможность создания DOM только для чтения с помощью Utf8JsonReader. Доступ к элементам JSON, составляющим полезные данные, можно получить с помощью типа JsonElement. Тип JsonElement предоставляет перечислители массивов и объектов вместе с API-интерфейсами для преобразования текста JSON в стандартные типы .NET. JsonDocument предоставляет свойство RootElement. Дополнительные сведения см. в разделе "Использование JsonDocument " далее в этой статье.

  • JsonNode и классы, производные от него в System.Text.Json.Nodes пространстве имен, обеспечивают возможность создания мутируемой модели DOM. Доступ к элементам JSON, составляющим полезные данные, можно получить через JsonNodeJsonObjectтипы , и JsonArrayJsonValueJsonElement типы. Дополнительные сведения смJsonNode. далее в этой статье.

При выборе между JsonDocument ими JsonNodeследует учитывать следующие факторы:

  • JsonNode DOM можно изменить после его создания. JsonDocument DOM неизменяем.
  • JsonDocument DOM обеспечивает быстрый доступ к своим данным.

Использование JsonNode

В следующем примере показано, как использовать JsonNode и другие типы в System.Text.Json.Nodes пространстве имен:

  • Создание DOM из строки JSON
  • Запись JSON из DOM.
  • Получение значения, объекта или массива из 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"
    }
}

Создание DOM JsonNode с инициализаторами объектов и внесение изменений

В приведенном ниже примере показано, как выполнить следующие задачи.

  • Создайте DOM с помощью инициализаторов объектов.
  • Внесите изменения в 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
        //    }
        //  }
        //}
    }
}

Десериализация подразделов полезных данных JSON

В следующем примере показано, как использовать JsonNode для перехода к подразделу дерева JSON и десериализации одного значения, пользовательского типа или массива из этого подраздела.

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

В следующем примере выбирается массив JSON, имеющий целые значения и вычисляющий среднее значение:

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

Предыдущий код:

  • Вычисляет среднее значение для объектов в массиве Students, имеющих свойство Grade.
  • Назначает значение по умолчанию 70 для учащихся, у которых нет оценки.
  • Возвращает число учащихся из Count свойства JsonArray.

JsonNode с JsonSerializerOptions

Можно использовать JsonSerializer для сериализации и десериализации экземпляра JsonNode. Однако при использовании перегрузки, которая принимает JsonSerializerOptions, экземпляр параметров используется только для получения пользовательских преобразователей. Другие функции экземпляра параметров не используются. Например, если задано JsonSerializerOptions.DefaultIgnoreCondition WhenWritingNull значение и вызов JsonSerializer с перегрузкой, которая принимает JsonSerializerOptions, свойства NULL не будут игнорироваться.

То же ограничение применяется к JsonNode методам, которые принимают JsonSerializerOptions параметр: WriteTo(Utf8JsonWriter, JsonSerializerOptions) и ToJsonString(JsonSerializerOptions). Эти API используются JsonSerializerOptions только для получения пользовательских преобразователей.

В следующем примере показан результат использования методов, которые принимают JsonSerializerOptions параметр и сериализуют 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; }
}

Если вам нужны функции, отличные JsonSerializerOptions от пользовательских преобразователей, используйте JsonSerializer строго типизированные целевые объекты (например Person , класс в этом примере), а не JsonNode.

Управление порядком свойств

JsonObject является одним из элементов полезных данных объекта JsonNode, который представляет мутируемый объект JSON. Несмотря на то, что тип моделируется как IDictionary<string, JsonNode>объект, где каждая запись является свойством объекта, она инкапсулирует неявный порядок свойств. Однако API, такие как Insert(Int32, String, JsonNode) и RemoveAt(Int32) эффективно моделировать тип как упорядоченный словарь, позволяя вставлять и удалять элементы по определенному индексу. Эти API позволяют изменять экземпляры объектов, которые могут напрямую влиять на порядок свойств.

В следующем коде показан пример добавления или перемещения определенного свойства в начало объекта.

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

Чтобы сравнить два JsonNode объекта для равенства, включая их потомки, используйте JsonNode.DeepEquals(JsonNode, JsonNode) этот метод.

Использование JsonDocument

В следующем примере показано, как использовать класс JsonDocument для произвольного доступа к данным в строке 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}")

Предыдущий код:

  • Предполагается, что анализируемый код JSON находится в строке jsonString.
  • Вычисляет среднее значение для объектов в массиве Students, имеющих свойство Grade.
  • Назначает значение по умолчанию 70 для учащихся, у которых нет оценки.
  • JsonDocument Создает экземпляр в инструкцииusing, так как JsonDocument реализуетсяIDisposable. После удаления экземпляра JsonDocument вы также теряете доступ ко всем его JsonElement экземплярам. Чтобы сохранить доступ к JsonElement экземпляру, создайте копию перед удалением родительского JsonDocument экземпляра. Чтобы сделать копию, вызовите JsonElement.Clone. Дополнительные сведения см. в разделе JsonDocument — IDisposable.

В предыдущем примере кода учащиеся учитываются путем увеличения переменной с каждой count итерацией. Альтернативой является вызов GetArrayLength, как показано в следующем примере:

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

Ниже приведен пример JSON, обрабатываемый этим кодом:

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

Аналогичный пример, который используется JsonNode вместо JsonDocumentпримера, см . в примере среднего класса JsonNode.

Поиск вложенных элементов в JsonDocument и JsonElement

Поиск по JsonElement запросу требует последовательного поиска свойств и, следовательно, относительно медленно (например, при использовании TryGetProperty). System.Text.Json предназначен для сокращения времени начального анализа, а не времени поиска. Поэтому рекомендуется использовать следующие подходы для оптимизации производительности при поиске по объекту JsonDocument:

  • Используйте встроенные перечислители (EnumerateArray и EnumerateObject) вместо создания собственных индексов или циклов.
  • Не выполняйте последовательный поиск по всему JsonDocument в каждом свойстве с помощью RootElement. Вместо этого выполните поиск по вложенным объектам JSON на основе известной структуры данных JSON. Например, предыдущие примеры кода ищут Grade свойство в Student объектах, циклируя Student объекты и получая значение Grade для каждого, а не выполняя поиск по всем JsonElement объектам, которые ищут Grade свойства. Выполнение последнего приведет к ненужным передачам одних и того же данных.

Сравнение JsonElements

Чтобы сравнить два JsonElement объекта для равенства, включая их потомки, используйте 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.

Использование JsonDocument для записи JSON

В следующем примере показано, как записать JSON код из 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()

Предыдущий код:

  • Считывает JSON-файл, загружает данные в JsonDocument и записывает форматированный (структурированный) код JSON в файл.
  • Использует JsonDocumentOptions, чтобы указать, что комментарии во входных данных JSON разрешены, но пропускаются.
  • По завершении для модуля записи вызывается Flush. В качестве альтернативы можно разрешить автоматическую запись в модуле записи при его удалении.

Ниже приведен пример входных данных JSON для обработки в примере кода:

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

В результате получаются следующие структурированные выходные данные JSON:

{
  "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 создает выполняющееся в памяти представление данных в едином буфере. JsonDocument Поэтому тип реализует IDisposable и должен использоваться внутри using блока.

Возвращайте JsonDocument из API только в том случае, если хотите передать владение временем существования и ответственность вызывающему объекту. В большинстве случаев это необязательно. Если вызывающему объекту необходимо работать со всем документом JSON, возвращайте Clone RootElement, то есть JsonElement. Если вызывающему объекту необходимо работать с определенным элементом в документе JSON, возвращайте Clone этого JsonElement. Если вы возвращаете RootElement или вложенный элемент напрямую, не выполняя Clone, вызывающий объект не сможет получить доступ к возвращаемому объекту JsonElement после того, как JsonDocument, которому он принадлежит, будет удален.

Ниже приведен пример, в котором необходимо сделать Clone.

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

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

Приведенный выше код ждет JsonElement, содержащий свойство fileName. Он открывает JSON-файл и создает JsonDocument. Метод предполагает, что вызывающий объект хочет работать со всем документом, поэтому он возвращает Clone RootElement.

Если вы получаете JsonElement и возвращаете вложенный элемент, нет необходимости возвращать Clone вложенного элемента. Вызывающий объект отвечает за поддержание активности JsonDocument, которому принадлежит переданный JsonElement. Например:

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

JsonDocument с JsonSerializerOptions

Можно использовать JsonSerializer для сериализации и десериализации экземпляра JsonDocument. Однако реализация для чтения и записи JsonDocument экземпляров с помощью JsonSerializer является оболочкой над JsonDocument.ParseValue(Utf8JsonReader) и JsonDocument.WriteTo(Utf8JsonWriter). Эта оболочка не перенаправит никакие JsonSerializerOptions (функции сериализатора) в Utf8JsonReader или Utf8JsonWriter. Например, если задано JsonSerializerOptions.DefaultIgnoreCondition WhenWritingNull значение и вызов JsonSerializer с перегрузкой, которая принимает JsonSerializerOptions, свойства NULL не будут игнорироваться.

В следующем примере показан результат использования методов, которые принимают JsonSerializerOptions параметр и сериализуют 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; }
}

Если вам нужны функции JsonSerializerOptions, используйте JsonSerializer строго типизированные целевые объекты (например Person , класс в этом примере), а не JsonDocument.

См. также