System.Text.Json에서 JSON 문서, Utf8JsonReader 및 Utf8JsonWriter를 사용하는 방법

이 문서에서는 다음 항목들을 사용하는 방법을 보여줍니다.

JSON DOM 선택

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

  • 역직렬화할 형식이 없는 경우
  • 수신하는 JSON에 고정된 스키마가 없는 경우 JSON을 검사하여 포함된 항목을 확인해야 합니다.

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

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

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;
        int count = 0;

        JsonNode document = JsonNode.Parse(jsonString)!;

        JsonNode root = document.Root;
        JsonArray studentsArray = root["Students"]!.AsArray();

        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 Person { 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}
        var 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를 사용합니다.

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 속성을 찾습니다. 후자의 검색을 수행하면 동일한 데이터에 대해 불필요한 전달 과정이 발생합니다.

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;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace JsonDocumentWithJsonSerializerOptions;

public class Program
{
    public static void Main()
    {
        Person person = new Person { 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}
        var 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를 사용합니다.

Utf8JsonWriter 사용

Utf8JsonWriterString, Int32DateTime과 같은 일반적인 .NET 형식에서 UTF-8 인코딩 JSON 텍스트를 쓸 수 있는 고성능 방법을 제공합니다. writer는 사용자 지정 직렬 변환기를 빌드하는 데 사용할 수 있는 하위 수준 형식입니다. JsonSerializer.Serialize 메서드는 내부적으로 Utf8JsonWriter를 사용합니다.

다음 예제에서는 Utf8JsonWriter 클래스 사용 방법을 보여줍니다.

var options = new JsonWriterOptions
{
    Indented = true
};

using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, options);

writer.WriteStartObject();
writer.WriteString("date", DateTimeOffset.UtcNow);
writer.WriteNumber("temp", 42);
writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(json);
Dim options As JsonWriterOptions = New JsonWriterOptions With {
    .Indented = True
}

Dim stream As MemoryStream = New MemoryStream
Dim writer As Utf8JsonWriter = New Utf8JsonWriter(stream, options)

writer.WriteStartObject()
writer.WriteString("date", DateTimeOffset.UtcNow)
writer.WriteNumber("temp", 42)
writer.WriteEndObject()
writer.Flush()

Dim json As String = Encoding.UTF8.GetString(stream.ToArray())
Console.WriteLine(json)

UTF-8 텍스트를 사용하여 쓰기

Utf8JsonWriter를 사용할 때 가능한 최상의 성능을 얻으려면 UTF-16 문자열이 아닌 UTF-8 텍스트로 이미 인코드된 JSON 페이로드를 쓰세요. UTF-16 문자열 리터럴을 사용하는 대신 JsonEncodedText를 사용하여 알려진 문자열 속성 이름 및 값을 정적으로 캐시 및 미리 인코딩하여 작성기에 전달하세요. 이 방법이 UTF-8 바이트 배열을 캐시하여 사용하는 것보다 빠릅니다.

이 방법은 사용자 지정 이스케이프를 수행해야 하는 경우에도 통합니다. System.Text.Json은 문자열을 작성하는 동안 이스케이프를 해제할 수 없습니다. 그러나 사용자 지정 JavaScriptEncoder를 작성기에 옵션으로 전달하거나, 자체 JavascriptEncoder를 사용하여 이스케이프를 수행하는 고유한 JsonEncodedText를 만든 후 문자열 대신 JsonEncodedText를 작성할 수 있습니다. 자세한 내용은 문자 인코딩 사용자 지정을 참조하세요.

원시 JSON 작성

일부 시나리오에서는 Utf8JsonWriter를 사용하여 만드는 JSON 페이로드에 ‘원시’ JSON을 작성할 수 있습니다. Utf8JsonWriter.WriteRawValue를 사용하여 이 작업을 수행할 수 있습니다. 일반적인 시나리오는 다음과 같습니다.

  • 새 JSON에 묶을 기존 JSON 페이로드가 있습니다.

  • 값의 서식을 기본 Utf8JsonWriter 서식과 다르게 지정하려고 합니다.

    예를 들어, 숫자 서식을 필요에 따라 사용자 지정할 수 있습니다. 기본적으로 System.Text.Json은 정수에 대한 소수점을 생략합니다. 예를 들어 1.0이 아닌 1을 작성합니다. 그 이유는 더 적은 바이트를 쓰는 것이 성능에 좋다는 것입니다. 그러나 JSON의 소비자가 10진수가 있는 숫자를 double로 처리하고 소수점이 없는 숫자를 정수로 처리한다고 가정해 보겠습니다. 정수에 대해 소수점과 0을 작성하여 배열의 숫자가 모두 double로 인식되도록 할 수 있습니다. 다음 예제에서는 해당 작업을 수행하는 방법을 보여줍니다.

    using System.Text;
    using System.Text.Json;
    
    namespace WriteRawJson;
    
    public class Program
    {
        public static void Main()
        {
            JsonWriterOptions writerOptions = new() { Indented = true, };
    
            using MemoryStream stream = new();
            using Utf8JsonWriter writer = new(stream, writerOptions);
    
            writer.WriteStartObject();
    
            writer.WriteStartArray("defaultJsonFormatting");
            foreach (double number in new double[] { 50.4, 51 })
            {
                writer.WriteStartObject();
                writer.WritePropertyName("value");
                writer.WriteNumberValue(number);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
    
            writer.WriteStartArray("customJsonFormatting");
            foreach (double result in new double[] { 50.4, 51 })
            {
                writer.WriteStartObject();
                writer.WritePropertyName("value");
                writer.WriteRawValue(
                    FormatNumberValue(result), skipInputValidation: true);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
    
            writer.WriteEndObject();
            writer.Flush();
    
            string json = Encoding.UTF8.GetString(stream.ToArray());
            Console.WriteLine(json);
        }
        static string FormatNumberValue(double numberValue)
        {
            return numberValue == Convert.ToInt32(numberValue) ? 
                numberValue.ToString() + ".0" : numberValue.ToString();
        }
    }
    // output:
    //{
    //  "defaultJsonFormatting": [
    //    {
    //      "value": 50.4
    //    },
    //    {
    //      "value": 51
    //    }
    //  ],
    //  "customJsonFormatting": [
    //    {
    //      "value": 50.4
    //    },
    //    {
    //      "value": 51.0
    //    }
    //  ]
    //}
    

문자 이스케이프 사용자 지정

JsonTextWriterStringEscapeHandling 설정은 모든 비 ASCII 문자 또는 HTML 문자를 이스케이프하는 옵션을 제공합니다. 기본적으로 Utf8JsonWriter는 모든 비 ASCII 문자 HTML 문자를 이스케이프합니다. 이러한 이스케이프는 심층 방어 보안을 위해 수행됩니다. 다른 이스케이프 정책을 지정하려면 JavaScriptEncoder를 만들고 JsonWriterOptions.Encoder을 설정하세요. 자세한 내용은 문자 인코딩 사용자 지정을 참조하세요.

null 값 쓰기

Utf8JsonWriter를 사용하여 null 값을 쓰려면 다음을 호출합니다.

  • WriteNull - Null을 사용하는 키-값 쌍을 값으로 씁니다.
  • WriteNullValue - Null을 JSON 배열의 요소로 씁니다.

문자열 속성의 경우 문자열이 Null이면 WriteStringWriteStringValueWriteNullWriteNullValue와 동일합니다.

Timespan, Uri 또는 char 값 쓰기

Timespan, Uri 또는 char 값을 작성하려면 (예를 들어 ToString()을 호출하여) 문자열로 그 값의 서식을 지정하고 WriteStringValue를 호출합니다.

Utf8JsonReader 사용

Utf8JsonReaderReadOnlySpan<byte> 또는 ReadOnlySequence<byte>에서 읽어온 UTF-8 인코딩 JSON 텍스트를 위한 고성능, 저할당, 전달 전용 판독기입니다. Utf8JsonReader는 사용자 지정 구문 분석기 및 역직렬 변환기를 빌드하는 데 활용할 수 있는 하위 수준 형식입니다. JsonSerializer.Deserialize 메서드는 내부적으로 Utf8JsonReader를 사용합니다.

Utf8JsonReader는 Visual Basic 코드에서 직접 사용할 수 없습니다. 자세한 내용은 Visual Basic 지원을 참조하세요.

다음 예제에서는 Utf8JsonReader 클래스 사용 방법을 보여줍니다.

var options = new JsonReaderOptions
{
    AllowTrailingCommas = true,
    CommentHandling = JsonCommentHandling.Skip
};
var reader = new Utf8JsonReader(jsonUtf8Bytes, options);

while (reader.Read())
{
    Console.Write(reader.TokenType);

    switch (reader.TokenType)
    {
        case JsonTokenType.PropertyName:
        case JsonTokenType.String:
            {
                string? text = reader.GetString();
                Console.Write(" ");
                Console.Write(text);
                break;
            }

        case JsonTokenType.Number:
            {
                int intValue = reader.GetInt32();
                Console.Write(" ");
                Console.Write(intValue);
                break;
            }

            // Other token types elided for brevity
    }
    Console.WriteLine();
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

위의 코드는 jsonUtf8 변수가 UTF-8로 인코딩된 유효한 JSON을 포함하는 바이트 배열이라고 가정합니다.

Utf8JsonReader를 사용하여 데이터 필터링

다음 예제는 파일을 동기적으로 읽고 값을 검색하는 방법을 보여줍니다.

using System.Text;
using System.Text.Json;

namespace SystemTextJsonSamples
{
    public class Utf8ReaderFromFile
    {
        private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
        private static ReadOnlySpan<byte> Utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF };

        public static void Run()
        {
            // ReadAllBytes if the file encoding is UTF-8:
            string fileName = "UniversitiesUtf8.json";
            ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);

            // Read past the UTF-8 BOM bytes if a BOM exists.
            if (jsonReadOnlySpan.StartsWith(Utf8Bom))
            {
                jsonReadOnlySpan = jsonReadOnlySpan.Slice(Utf8Bom.Length);
            }

            // Or read as UTF-16 and transcode to UTF-8 to convert to a ReadOnlySpan<byte>
            //string fileName = "Universities.json";
            //string jsonString = File.ReadAllText(fileName);
            //ReadOnlySpan<byte> jsonReadOnlySpan = Encoding.UTF8.GetBytes(jsonString);

            int count = 0;
            int total = 0;

            var reader = new Utf8JsonReader(jsonReadOnlySpan);

            while (reader.Read())
            {
                JsonTokenType tokenType = reader.TokenType;

                switch (tokenType)
                {
                    case JsonTokenType.StartObject:
                        total++;
                        break;
                    case JsonTokenType.PropertyName:
                        if (reader.ValueTextEquals(s_nameUtf8))
                        {
                            // Assume valid JSON, known schema
                            reader.Read();
                            if (reader.GetString()!.EndsWith("University"))
                            {
                                count++;
                            }
                        }
                        break;
                }
            }
            Console.WriteLine($"{count} out of {total} have names that end with 'University'");
        }
    }
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

이 예제의 비동기 버전은 .NET 샘플 JSON 프로젝트를 참조하세요.

위의 코드는

  • JSON에 개체 배열이 포함되고 각 개체에 문자열 형식의 "name" 속성이 포함될 수 있다고 가정합니다.

  • "대학교"로 끝나는 개체 및 "이름" 속성 값의 개수를 계산합니다.

  • 파일이 UTF-16으로 인코딩되고 UTF-8로 코드 변환된다고 가정합니다. UTF-8로 인코딩된 파일은 다음 코드를 사용하여 ReadOnlySpan<byte>로 직접 읽을 수 있습니다.

    ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);
    

    파일에 UTF-8 BOM(바이트 순서 표시)이 포함된 경우 제거한 후 바이트를 Utf8JsonReader에 전달해야 합니다. 판독기에는 텍스트가 필요하기 때문입니다. 그렇지 않으면 BOM은 잘못된 JSON으로 간주되고, 판독기에서 예외를 throw합니다.

다음은 위의 코드에서 읽을 수 있는 JSON 샘플입니다. 다음과 같이 "4개 이름 중 2개가 '대학교'로 끝나는 이름"이라는 결과 요약 메시지가 표시됩니다.

[
  {
    "web_pages": [ "https://contoso.edu/" ],
    "alpha_two_code": "US",
    "state-province": null,
    "country": "United States",
    "domains": [ "contoso.edu" ],
    "name": "Contoso Community College"
  },
  {
    "web_pages": [ "http://fabrikam.edu/" ],
    "alpha_two_code": "US",
    "state-province": null,
    "country": "United States",
    "domains": [ "fabrikam.edu" ],
    "name": "Fabrikam Community College"
  },
  {
    "web_pages": [ "http://www.contosouniversity.edu/" ],
    "alpha_two_code": "US",
    "state-province": null,
    "country": "United States",
    "domains": [ "contosouniversity.edu" ],
    "name": "Contoso University"
  },
  {
    "web_pages": [ "http://www.fabrikamuniversity.edu/" ],
    "alpha_two_code": "US",
    "state-province": null,
    "country": "United States",
    "domains": [ "fabrikamuniversity.edu" ],
    "name": "Fabrikam University"
  }
]

디코딩된 JSON 문자열 사용

.NET 7부터는 디코딩된 JSON 문자열을 사용하기 위해 Utf8JsonReader.GetString() 대신 Utf8JsonReader.CopyString 메서드를 사용할 수 있습니다. 항상 새 문자열을 할당하는 GetString()과 달리, CopyString은 현재 소유한 버퍼에 이스케이프되지 않은 문자열을 복사할 수 있습니다. 다음 코드 조각은 CopyString을 사용하여 UTF-16 문자열을 사용하는 예제를 보여줍니다.

int valueLength = reader.HasReadOnlySequence 
    ? checked((int)ValueSequence.Length) 
    : ValueSpan.Length;

char[] buffer = ArrayPool<char>.Shared.Rent(valueLength);
int charsRead = reader.CopyString(buffer);
ReadOnlySpan<char> source = buffer.Slice(0, charsRead);

// Handle the unescaped JSON string.
ParseUnescapedString(source);
ArrayPool<char>.Shared.Return(buffer, clearArray: true);

Utf8JsonReader를 사용하여 스트림에서 읽기

큰 파일(예: 기가바이트 이상 크기)을 읽을 때 전체 파일을 한 번에 메모리에 로드하지 않아도 되는 경우가 있습니다. 이 시나리오에서는 FileStream을 사용할 수 있습니다.

Utf8JsonReader를 사용하여 스트림에서 읽는 경우 다음 규칙이 적용됩니다.

  • 부분 JSON 페이로드를 포함하는 버퍼는 판독기가 진행할 수 있도록 최소한 그 안에 있는 가장 큰 JSON 토큰만큼 커야 합니다.
  • 버퍼는 최소한 JSON 내에서 가장 큰 공백 시퀀스만큼 커야 합니다.
  • 판독기는 JSON 페이로드의 다음 TokenType을 완전히 읽을 때까지는 읽은 데이터를 추적하지 않습니다. 따라서 버퍼에 바이트가 남아 있으면 판독기에 다시 전달해야 합니다. BytesConsumed를 사용하여 남은 바이트 수를 확인할 수 있습니다.

다음 코드에서는 스트림에서 읽는 방법을 보여 줍니다. 이 예제에서는 MemoryStream을 보여 줍니다. FileStream이 시작 시 UTF-8 BOM을 포함하는 경우를 제외하고는 FileStream에서 비슷한 코드가 작동합니다. 이 경우 남은 바이트를 Utf8JsonReader에 전달하기 전에 버퍼에서 3바이트를 제거해야 합니다. 그렇지 않으면 BOM이 JSON의 유효한 부분으로 간주되지 않으므로 판독기가 예외를 throw합니다.

이 샘플 코드는 4KB 버퍼로 시작하며 크기가 작아 전체 JSON 토큰을 수용할 수 없을 때마다 버퍼 크기를 두 배로 늘립니다. 이는 판독기가 JSON 페이로드에 대한 작업을 진행하는 데 필요합니다. 이 코드 조각에 제공된 JSON 샘플은 10바이트와 같이 매우 작은 초기 버퍼 크기를 설정하는 경우에만 버퍼 크기 증가를 트리거합니다. 초기 버퍼 크기를 10으로 설정하는 경우 Console.WriteLine 문은 버퍼 크기 증가의 원인과 영향을 보여 줍니다. 4KB 초기 버퍼 크기에서 전체 샘플 JSON은 각 Console.WriteLine마다 표시되며 버퍼 크기를 늘릴 필요가 없습니다.

using System.Text;
using System.Text.Json;

namespace SystemTextJsonSamples
{
    public class Utf8ReaderPartialRead
    {
        public static void Run()
        {
            var jsonString = @"{
                ""Date"": ""2019-08-01T00:00:00-07:00"",
                ""Temperature"": 25,
                ""TemperatureRanges"": {
                    ""Cold"": { ""High"": 20, ""Low"": -10 },
                    ""Hot"": { ""High"": 60, ""Low"": 20 }
                },
                ""Summary"": ""Hot"",
            }";

            byte[] bytes = Encoding.UTF8.GetBytes(jsonString);
            var stream = new MemoryStream(bytes);

            var buffer = new byte[4096];

            // Fill the buffer.
            // For this snippet, we're assuming the stream is open and has data.
            // If it might be closed or empty, check if the return value is 0.
            stream.Read(buffer);

            // We set isFinalBlock to false since we expect more data in a subsequent read from the stream.
            var reader = new Utf8JsonReader(buffer, isFinalBlock: false, state: default);
            Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");

            // Search for "Summary" property name
            while (reader.TokenType != JsonTokenType.PropertyName || !reader.ValueTextEquals("Summary"))
            {
                if (!reader.Read())
                {
                    // Not enough of the JSON is in the buffer to complete a read.
                    GetMoreBytesFromStream(stream, buffer, ref reader);
                }
            }

            // Found the "Summary" property name.
            Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
            while (!reader.Read())
            {
                // Not enough of the JSON is in the buffer to complete a read.
                GetMoreBytesFromStream(stream, buffer, ref reader);
            }
            // Display value of Summary property, that is, "Hot".
            Console.WriteLine($"Got property value: {reader.GetString()}");
        }

        private static void GetMoreBytesFromStream(
            MemoryStream stream, byte[] buffer, ref Utf8JsonReader reader)
        {
            int bytesRead;
            if (reader.BytesConsumed < buffer.Length)
            {
                ReadOnlySpan<byte> leftover = buffer.AsSpan((int)reader.BytesConsumed);

                if (leftover.Length == buffer.Length)
                {
                    Array.Resize(ref buffer, buffer.Length * 2);
                    Console.WriteLine($"Increased buffer size to {buffer.Length}");
                }

                leftover.CopyTo(buffer);
                bytesRead = stream.Read(buffer.AsSpan(leftover.Length));
            }
            else
            {
                bytesRead = stream.Read(buffer);
            }
            Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
            reader = new Utf8JsonReader(buffer, isFinalBlock: bytesRead == 0, reader.CurrentState);
        }
    }
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

앞의 예제에서는 버퍼 크기를 늘릴 수 있는 크기 제한을 설정하지 않습니다. 토큰 크기가 너무 클 경우 코드는 OutOfMemoryException 예외와 함께 실패할 수 있습니다. 이 문제는 JSON이 크기가 약 1GB 이상인 토큰을 포함하는 경우 발생할 수 있습니다. 1GB 크기가 두 배가 되면 int32 버퍼에 비해 너무 커지기 때문입니다.

Utf8JsonReader는 ref struct

Utf8JsonReader 형식은 ref struct이므로 특정 제한이 있습니다. 예를 들어 ref struct가 아닌 클래스 또는 구조체에 필드로 저장할 수 없습니다. 고성능을 얻으려면 입력 ReadOnlySpan<byte>를 캐시해야 하는데 그 자체가 ref struct이므로 이 형식은 ref struct여야 합니다. 또한 이 형식은 상태를 유지하기 때문에 변경 가능합니다. 따라서 값이 아닌 참조로 전달해야 합니다. 값으로 전달하면 구조체 복사본이 생성되고 상태 변경 내용이 호출자에게 표시되지 않습니다. ref 구조체를 사용하는 방법에 대한 자세한 내용은 할당 방지를 참조하세요.

UTF-8 텍스트 읽기

Utf8JsonReader를 사용할 때 가능한 최상의 성능을 얻으려면 UTF-16 문자열이 아닌 UTF-8 텍스트로 이미 인코딩된 JSON 페이로드를 읽으세요. 코드 예제는 Utf8JsonReader를 사용하여 데이터 필터링을 참조하세요.

다중 세그먼트 ReadOnlySequence를 사용하여 읽기

JSON 입력이 ReadOnlySpan<byte>이면 읽기 루프를 진행할 때 판독기의 ValueSpan 속성에서 각 JSON 요소에 액세스할 수 있습니다. 그러나 입력이 ReadOnlySequence<byte>(PipeReader에서 읽은 결과)인 경우에는 일부 JSON 요소가 ReadOnlySequence<byte> 개체의 여러 세그먼트에 걸쳐 있을 수 있습니다. 이러한 요소는 연속 메모리 블록의 ValueSpan에서 액세스할 수 없습니다. 그 대신, 다중 세그먼트 ReadOnlySequence<byte>가 입력으로 사용될 때마다 판독기에서 HasValueSequence 속성을 폴링하여 현재 JSON 요소에 액세스하는 방법을 확인하세요. 다음은 권장 패턴입니다.

while (reader.Read())
{
    switch (reader.TokenType)
    {
        // ...
        ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
            reader.ValueSequence.ToArray() :
            reader.ValueSpan;
        // ...
    }
}

속성 이름 조회에 ValueTextEquals 사용

ValueSpan을 사용하여 바이트 단위 비교를 수행하려면 속성 이름 조회를 위한 SequenceEqual을 호출하지 마세요. 그 대신 JSON에서 이스케이프된 모든 문자를 이스케이프 해제하는 ValueTextEquals를 호출하세요. 다음은 "name"이라는 속성을 검색하는 방법을 보여주는 예제입니다.

private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
while (reader.Read())
{
    switch (reader.TokenType)
    {
        case JsonTokenType.StartObject:
            total++;
            break;
        case JsonTokenType.PropertyName:
            if (reader.ValueTextEquals(s_nameUtf8))
            {
                count++;
            }
            break;
    }
}

Null 값을 null 허용 값 형식으로 읽기

기본 제공 System.Text.Json API는 null을 허용하지 않는 값 형식만 반환합니다. 예를 들어 Utf8JsonReader.GetBooleanbool을 반환합니다. JSON에서 Null을 발견하면 예외를 throw합니다. 다음 예제에서는 Null을 처리하는 두 가지 방법을 보여줍니다. 하나는 null 허용 값 형식을 반환하는 방법이고, 다른 하나는 기본값을 반환하는 방법입니다.

public bool? ReadAsNullableBoolean()
{
    _reader.Read();
    if (_reader.TokenType == JsonTokenType.Null)
    {
        return null;
    }
    if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
    {
        throw new JsonException();
    }
    return _reader.GetBoolean();
}
public bool ReadAsBoolean(bool defaultValue)
{
    _reader.Read();
    if (_reader.TokenType == JsonTokenType.Null)
    {
        return defaultValue;
    }
    if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
    {
        throw new JsonException();
    }
    return _reader.GetBoolean();
}

참고 항목