System.Text.Json에서 오버플로 JSON을 처리하거나 JsonElement 또는 JsonNode를 사용하는 방법

이 문서에서는 System.Text.Json 네임스페이스를 사용하여 오버플로 JSON을 처리하는 방법을 보여줍니다. 또한 대상 형식이 역직렬화되는 모든 JSON과 완벽하게 일치하지 않을 수 있는 다른 시나리오의 대안으로 JsonElement 또는 JsonNode로 역직렬화하는 방법도 보여줍니다.

오버플로 JSON 처리

역직렬화할 때 대상 형식의 속성으로 표시되지 않는 데이터를 JSON에서 받을 수 있습니다. 예를 들어 대상 형식이 다음과 같다고 가정하겠습니다.

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}
Public Class WeatherForecast
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As String
End Class

그리고 역직렬화할 JSON은 다음과 같습니다.

{
  "Date": "2019-08-01T00:00:00-07:00",
  "temperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

표시된 JSON을 표시된 형식으로 역직렬화하면 DatesAvailableSummaryWords 속성은 이동할 곳이 없으므로 없어집니다. 이러한 속성과 같은 추가 데이터를 캡처하려면 다음과 같이 JsonExtensionData 특성을 Dictionary<string,object> 또는 Dictionary<string,JsonElement> 형식의 속성에 적용합니다.

public class WeatherForecastWithExtensionData
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
    [JsonExtensionData]
    public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
Public Class WeatherForecastWithExtensionData
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As String

    <JsonExtensionData>
    Public Property ExtensionData As Dictionary(Of String, Object)

End Class

다음 표에서는 이전에 표시된 JSON을 이 샘플 형식으로 역직렬화한 결과를 보여줍니다. 추가 데이터는 ExtensionData 속성의 키-값 쌍이 됩니다.

속성 참고
Date "8/1/2019 12:00:00 AM -07:00"
TemperatureCelsius 0 대/소문자를 구분하는 불일치(JSON의 temperatureCelsius). 따라서 속성이 설정되지 않습니다.
Summary "Hot"
ExtensionData "temperatureCelsius": 25,
"DatesAvailable": ["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],
"SummaryWords": ["Cool","Windy","Humid"]
대/소문자가 일치하지 않으므로 temperatureCelsius는 추가 속성이며 사전에서 키-값 쌍이 됩니다.
JSON의 추가 배열은 각각 키-값 쌍이 되고, 배열은 값 개체로 사용됩니다.

대상 개체가 직렬화되면 확장 데이터 키 값 쌍은 마치 수신 JSON에 있는 것처럼 JSON 속성이 됩니다.

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 0,
  "Summary": "Hot",
  "temperatureCelsius": 25,
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

ExtensionData 속성 이름은 JSON에 표시되지 않습니다. 이 동작을 통해 JSON은 추가 데이터를 잃지 않고도 라운드트립을 수행할 수 있으며, 이 동작이 없으면 추가 데이터가 역직렬화되지 않습니다.

다음 예제에서는 JSON에서 역직렬화된 개체로 이동해 다시 JSON으로 복귀하는 왕복을 보여줍니다.

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

namespace RoundtripExtensionData
{
    public class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
        [JsonExtensionData]
        public Dictionary<string, JsonElement>? ExtensionData { get; set; }
    }

    public class Program
    {
        public static void Main()
        {
            string jsonString =
@"{
  ""Date"": ""2019-08-01T00:00:00-07:00"",
  ""temperatureCelsius"": 25,
  ""Summary"": ""Hot"",
  ""SummaryField"": ""Hot"",
  ""DatesAvailable"": [
    ""2019-08-01T00:00:00-07:00"",
    ""2019-08-02T00:00:00-07:00""
  ],
  ""SummaryWords"": [
    ""Cool"",
    ""Windy"",
    ""Humid""
  ]
}";
            WeatherForecast weatherForecast = 
                JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;

            var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
            jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
            Console.WriteLine($"JSON output:\n{jsonString}\n");
        }
    }
}
// output:
//JSON output:
//{
//  "Date": "2019-08-01T00:00:00-07:00",
//  "TemperatureCelsius": 0,
//  "Summary": "Hot",
//  "temperatureCelsius": 25,
//  "SummaryField": "Hot",
//  "DatesAvailable": [
//    "2019-08-01T00:00:00-07:00",
//    "2019-08-02T00:00:00-07:00"
//  ],
//  "SummaryWords": [
//    "Cool",
//    "Windy",
//    "Humid"
//  ]
//}

JsonElement 또는 JsonNode로 역직렬화

특정 속성에 대해 허용되는 JSON을 유연하게 사용하려는 경우 JsonElement 또는 JsonNode로 역직렬화하는 것이 대안입니다. 유효한 JSON 속성은 JsonElement 또는 JsonNode로 역직렬화할 수 있습니다. 변경할 수 없는 개체를 만들려면 JsonElement를 선택하고 변경 가능한 개체를 만들려면 JsonNode를 선택합니다.

다음 예제에서는 형식 JsonElementJsonNode의 속성을 포함하는 클래스에 대해 JSON에서 출발해 JSON으로 복귀하는 왕복을 보여줍니다.

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

namespace RoundtripJsonElementAndNode
{
    public class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
        public JsonElement DatesAvailable { get; set; }
        public JsonNode? SummaryWords { get; set; }
    }
        public class Program
    {
        public static void Main()
        {
            string jsonString =
@"{
  ""Date"": ""2019-08-01T00:00:00-07:00"",
  ""TemperatureCelsius"": 25,
  ""Summary"": ""Hot"",
  ""DatesAvailable"": [
    ""2019-08-01T00:00:00-07:00"",
    ""2019-08-02T00:00:00-07:00""
  ],
  ""SummaryWords"": [
    ""Cool"",
    ""Windy"",
    ""Humid""
  ]
}";
            WeatherForecast? weatherForecast = 
                JsonSerializer.Deserialize<WeatherForecast>(jsonString);

            var serializeOptions = new JsonSerializerOptions { WriteIndented = true };
            jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
            Console.WriteLine(jsonString);
        }
    }
}
// output:
//{
//  "Date": "2019-08-01T00:00:00-07:00",
//  "TemperatureCelsius": 25,
//  "Summary": "Hot",
//  "DatesAvailable": [
//    "2019-08-01T00:00:00-07:00",
//    "2019-08-02T00:00:00-07:00"
//  ],
//  "SummaryWords": [
//    "Cool",
//    "Windy",
//    "Humid"
//  ]
//}

참고 항목