다음을 통해 공유


System.Text.Json에서 JSON 문서 개체 모델을 사용하는 방법

이 문서에서는 JSON 페이로드의 데이터에 임의로 액세스하기 위해 JSON DOM(문서 개체 모델)을 사용하는 방법을 보여 줍니다.

JSON DOM 선택

DOM을 사용하는 것은 다음의 경우 JsonSerializer를 사용한 역직렬화의 대안입니다.

  • 역직렬화할 형식이 없습니다.
  • 수신한 JSON에는 고정된 스키마가 없으며 포함된 내용을 확인하려면 검사해야 합니다.

System.Text.Json은 JSON DOM을 빌드하는 두 가지 방법을 다음과 같이 제공합니다.

  • JsonDocumentUtf8JsonReader를 사용하여 읽기 전용 DOM을 빌드하는 기능을 제공합니다. 페이로드를 구성하는 JSON 요소는 JsonElement 형식을 통해 액세스할 수 있습니다. JsonElement 형식은 JSON 텍스트를 일반적인 .NET 형식으로 변환하는 API와 함께 배열 및 개체 열거자를 제공합니다. JsonDocumentRootElement 속성을 노출합니다. 자세한 내용은 이 문서의 뒷부분에서 JsonDocument 사용을 참조하세요.

  • System.Text.Json.Nodes 네임스페이스에서 파생되는 클래스와 JsonNode는 변경 가능한 DOM을 만드는 기능을 제공합니다. 페이로드를 구성하는 JSON 요소는 JsonNode, JsonObject, JsonArray, JsonValueJsonElement 형식을 통해 액세스할 수 있습니다. 자세한 내용은 이 문서의 뒷부분에서 JsonNode 사용을 참조하세요.

JsonDocumentJsonNode 중에서 하나를 선택할 때 다음 요소를 고려합니다.

  • JsonNode DOM을 만든 후에 변경할 수 있습니다. JsonDocument DOM은 변경할 수 없습니다.
  • JsonDocument DOM은 데이터에 더 빠르게 액세스할 수 있도록 합니다.

JsonNode 사용

다음 예제에서는 JsonNode를 사용하는 방법과 System.Text.Json.Nodes 네임스페이스의 다른 형식을 사용하여 다음을 수행하는 방법을 보여줍니다.

  • JSON 문자열에서 DOM 만들기
  • DOM에서 JSON을 작성합니다.
  • 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"
    }
}

개체 이니셜라이저를 사용하여 JsonNode DOM 만들기 및 변경

아래 예제는 다음과 같은 작업의 방법을 보여 줍니다.

  • 개체 이니셜라이저를 사용하여 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

위의 코드는

  • Grade 속성이 있는 Students 배열의 개체에 대한 평균 등급을 계산합니다.
  • 등급이 없는 학생에게 기본 등급 70을 할당합니다.
  • JsonArrayCount 속성에서 학생 수를 가져옵니다.

JsonNode(JsonSerializerOptions 사용)

JsonSerializer를 사용하여 JsonNode 인스턴스를 직렬화하고 역직렬화할 수 있습니다. 그러나 JsonSerializerOptions를 사용하는 오버로드를 사용하는 경우 옵션 인스턴스는 사용자 지정 변환기를 가져오는 데만 사용됩니다. 옵션 인스턴스의 나머지 기능은 사용되지 않습니다. 예를 들어 JsonSerializerOptions.DefaultIgnoreConditionWhenWritingNull로 설정하고 JsonSerializerOptions를 사용하는 오버로드를 사용하여 JsonSerializer를 호출하는 경우 null 속성은 무시되지 않습니다.

JsonSerializerOptions 매개 변수를 사용하는 JsonNode 메서드인 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의 기능이 필요한 경우 JsonNode 대신 강력한 형식의 대상(예: 이 예제의 Person 클래스)과 함께 JsonSerializer를 사용합니다.

속성 순서 조작

JsonObject 는 페이로드의 JsonNode요소 중 하나이며 변경 가능한 JSON 개체를 나타냅니다. 형식은 각 항목이 개체의 속성인 형식으로 IDictionary<string, JsonNode>모델링되더라도 암시적 속성 순서를 캡슐화합니다. 그러나 특정 인덱스에서 항목을 삽입하고 RemoveAt(Int32) 제거할 수 있도록 하여 정렬된 사전과 같은 Insert(Int32, String, JsonNode) API에서 형식을 효과적으로 모델링합니다. 이러한 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이라는 문자열에 있다고 가정합니다.
  • Grade 속성이 있는 Students 배열의 개체에 대한 평균 등급을 계산합니다.
  • 등급이 없는 학생에게 기본 등급 70을 할당합니다.
  • JsonDocumentIDisposable을 구현하기 때문에 usingJsonDocument 인스턴스를 만듭니다. JsonDocument 인스턴스가 삭제되면 모든 JsonElement 인스턴스에 대한 액세스 권한도 잃게 됩니다. JsonElement 인스턴스에 대한 액세스를 유지하려면 이 인스턴스의 복사본을 만든 후 부모 JsonDocument 인스턴스를 삭제하세요. 복사본을 만들려면 JsonElement.Clone을 호출합니다. 자세한 내용은 JsonDocument is 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
}

JsonDocument 대신 JsonNode를 사용하는 유사한 예제는 JsonNode 평균 등급 예제를 참조하세요.

하위 요소의 JsonDocument 및 JsonElement를 검색하는 방법

JsonElement에서 검색하려면 속성을 순차적으로 검색해야 하므로 검색 속도가 비교적 느립니다(예: TryGetProperty를 사용하는 경우). System.Text.Json은 조회 시간이 아닌 초기 구문 분석 시간을 최소화하도록 설계되었습니다. 따라서 JsonDocument 개체를 검색할 때 성능을 최적화하려면 다음 방법을 사용하세요.

  • 자체적으로 인덱싱 또는 루프를 수행하지 말고 기본 제공 열거자(EnumerateArrayEnumerateObject)를 사용합니다.
  • RootElement를 사용하여 전체 JsonDocument의 모든 속성을 순차적으로 검색하지 마세요. 그 대신, 알려진 JSON 데이터 구조체를 기반으로 중첩된 JSON 개체를 검색합니다. 예를 들어 앞의 코드 예제에서는 Grade 속성을 찾는 모든 JsonElement 개체를 검색하는 대신 Student 개체를 반복하고 각각에 대한 Grade 값을 가져오면 Student 개체에서 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 작성

다음 예제에서는 JsonDocument에서 JSON을 쓰는 방법을 보여줍니다.

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의 주석을 허용하지만 무시하도록 지정합니다.
  • 완료되면 writer에서 Flush를 호출합니다. 삭제될 때 writer가 자동으로 플러시하도록 설정하는 방법도 있습니다.

다음은 예제 코드에서 처리할 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 블록 내에서 사용해야 합니다.

수명 소유권 및 폐기 책임을 호출자에 양도하려는 경우에만 API에서 JsonDocument를 반환하세요. 대부분의 시나리오에서는 이렇게 할 필요가 없습니다. 호출자가 전체 JSON 문서를 사용해야 하는 경우 JsonElementRootElementClone을 반환하세요. 호출자가 JSON 문서 내의 특정 요소를 사용해야 하는 경우 해당 JsonElementClone을 반환하세요. Clone을 만들지 않고 RootElement 또는 하위 요소를 직접 반환하면 해당 소유권을 가진 JsonDocument가 폐기된 후에 반환되는 JsonElement에 호출자가 액세스할 수 없습니다.

다음은 Clone을 만들어야 하는 예제입니다.

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

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

위의 코드에는 fileName 속성을 포함하는 JsonElement가 필요합니다. 위의 코드는 JSON 파일을 열고 JsonDocument를 만듭니다. 이 메서드는 호출자가 전체 문서를 사용하려 한다고 가정하고 RootElementClone을 반환합니다.

JsonElement를 수신하고 하위 요소를 반환하는 경우에는 하위 요소의 Clone을 반환할 필요가 없습니다. 호출자는 전달된 JsonElement가 속한 JsonDocument를 활성 상태로 유지해야 합니다. 예를 들어:

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

JsonDocument(JsonSerializerOptions 사용)

JsonSerializer를 사용하여 JsonDocument 인스턴스를 직렬화하고 역직렬화할 수 있습니다. 그러나 JsonSerializer를 사용하여 JsonDocument 인스턴스를 읽고 쓰기 위한 구현은 JsonDocument.ParseValue(Utf8JsonReader)JsonDocument.WriteTo(Utf8JsonWriter)에 대한 래퍼입니다. 이 래퍼는 Utf8JsonReader 또는 Utf8JsonWriterJsonSerializerOptions(직렬 변환기 기능)를 전달하지 않습니다. 예를 들어 JsonSerializerOptions.DefaultIgnoreConditionWhenWritingNull로 설정하고 JsonSerializerOptions를 사용하는 오버로드를 사용하여 JsonSerializer를 호출하는 경우 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의 기능이 필요한 경우 JsonDocument 대신 강력한 형식의 대상(예: 이 예제의 Person 클래스)과 함께 JsonSerializer를 사용합니다.

참고 항목