본문 바로가기
C#

C#] System.Text.Json vs Newtonsoft.Json 차이점 비교

by Fastlane 2022. 7. 28.
728x90
반응형

MS는 2019년 .Net Core 3.0과 함께 System.Text.Json namespace를 release했다. 

Newtonsoft.Json 에서 System.Text.Json으로 migration을 고려하게 되었다. 

Newtonsoft.Json과 비교하며, migration 장단점을 확인해보자. 

 

Why a new System.Text.Json library?

Nowtonsoft.Json은 오랫동안 .Net developer에게 JSON의 serializing, deserializing용도로 친숙하게 사용되었다. System.Text.Json이 새롭게 소개된 배경에는 performance, security가 있다. 

System.Text.Json은 .Net Core 3.X 부터는 runtime에 포함되어 있고, 이전 버전에서는 System.Text.Json nugget package를 설치해야 한다. 

 

System.Text.Json 지원버전 : 

  • .NET Standard 2.0 and later
  • .NET Framework 4.7.2 and later
  • .NET Core 2.1 and later
  • .NET 5 and later

Newtonsoft.Json과 System.Text.Json의 차이점

1. Case-insensitive deserialization (대소문자 구분)

using System.Text.Json;
using Newtonsoft.Json;
    public class WeatherForecast
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string jsonString = "{\"date\":\"2019-08-01\",\"temperatureCelsius\":25,\"summary\":\"Hot\"}";
            var weatherForecast = System.Text.Json.JsonSerializer.Deserialize<WeatherForecast>(jsonString);
            var weatherForecast2 = Newtonsoft.Json.JsonConvert.DeserializeObject<WeatherForecast>(jsonString);
        }
    }

Newtonsoft.Json은 기본으로 case-insensitive하다. System.Text.Json은 기본으로 case-sensitive하므로, 더 정확한 match가 가능하다. camel case JSON을 Pascal case property name으로 deserialize할때 System.Text.Json은 match되지 않는다. 

 JsonSerializerOptions.PropertyNameCaseInsensitive를 true로 하여, case-insensitive하게 변경할 수 있다. 

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
string jsonString = "{\"date\":\"2019-08-01\",\"temperatureCelsius\":25,\"summary\":\"Hot\"}";
var weatherForecast = System.Text.Json.JsonSerializer.Deserialize<WeatherForecast>(jsonString, options);

2. Allow comments and Trailing commas

string jsonString = "{\"Date\":\"2019-08-01\",\"TemperatureCelsius\":25,\"Summary\":\"Hot\",}";
var weatherForecast2 = Newtonsoft.Json.JsonConvert.DeserializeObject<WeatherForecast>(jsonString);
var weatherForecast = System.Text.Json.JsonSerializer.Deserialize<WeatherForecast>(jsonString);

jsonString에 trailing commas가 있을때 빌드 시, System.Text.Json은 아래의 에러를 반환한다. Newtonsoft.Json은 trailing commas를 무시하고 deserialize한다. 

System.Text.Json.JsonException: 'The JSON object contains a trailing comma at the end which is not supported in this mode. Change the reader options. Path: $ | LineNumber: 0 | BytePositionInLine: 61.'

 

아래와 같이 option을 사용해서, comments와 trailing commas를 허용할 수 있다. 

var options = new JsonSerializerOptions
{
   ReadCommentHandling = JsonCommentHandling.Skip,
   AllowTrailingCommas = true,
};
string jsonString = "{\"Date\":\"2019-08-01\",\"TemperatureCelsius\":25,\"Summary\":\"Hot\",}";
var weatherForecast = System.Text.Json.JsonSerializer.Deserialize<WeatherForecast>(jsonString, options);

3. Character escaping

Newtonsoft.Json은 character를 escape하지 않고, 그대로 serialize한다. 

System.Text.Json은 기본으로 모든 non-ASCII 문자와 HTML-sensitive characters를 escape한다. 따라서, XSS 공격으로부터 보호할 수 있다. 

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

class Program
{
    static void Main(string[] args)
    {

        // Serialize
        WeatherForecast weather = new WeatherForecast { 
            Date = Convert.ToDateTime("2019-08-01"), 
            TemperatureCelsius = 25, 
            Summary = "<script>alert('жарко');</script>" };

        string jsonString = System.Text.Json.JsonSerializer.Serialize(weather);
        string jsonString2 = JsonConvert.SerializeObject(weather);

        Console.WriteLine(jsonString);
        //{ "Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"\u003Cscript\u003Ealert(\u0027\u0436\u0430\u0440\u043A\u043E\u0027);\u003C/script\u003E"}

        Console.WriteLine(jsonString2);
        //{ "Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"жарко"}

    }
}

아래와 같이 option을 사용해서, escape하지 않을 수 있다. 

using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Unicode;

키릴 자모, 그리스어 문자를 escape하지 않는다. 모든 언어를 escape하지 않으려면 UnicodeRanges.All을 사용한다. 

var options = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic),
    WriteIndented = true
};

// Serialize
WeatherForecast weather = new WeatherForecast { Date = Convert.ToDateTime("2019-08-01"), TemperatureCelsius = 25, Summary = "жарко" };
string jsonString = System.Text.Json.JsonSerializer.Serialize(weather, options);

/*
{
  "Date": "2019-08-01T00:00:00",
  "TemperatureCelsius": 25,
  "Summary": "жарко"
}
*/

4. Maximum depth

System.Text.Json은 64 depth 제한이 있다. 

제한이 없도록 하려면 JsonSerializerOptions.MaxDepth 옵션을 사용한다. 

 

5. Serialize and Deserialize fields

Newtonsoft.Json은 기본으로 fields도 serialize, deserialize한다. 

System.Text.Json은 기본으로 fields는 포함하지 않는다. 

public class WeatherForecast
{
    public DateTime Date;
    public int TemperatureCelsius;
    public string? Summary;
}

class Program
{
    static void Main(string[] args)
    {
        string jsonString = "{\"date\":\"2019-08-01\",\"temperatureCelsius\":25,\"summary\":\"Hot\"}";
        var weatherForecast = System.Text.Json.JsonSerializer.Deserialize<WeatherForecast>(jsonString);
        var weatherForecast2 = Newtonsoft.Json.JsonConvert.DeserializeObject<WeatherForecast>(jsonString);

    }
}

포함하려면 JsonSerializerOptions.IncludeFields 옵션을 사용한다. 

 

6. Null value handling

serialize할때, Newtonsoft.Json은 null values를 무시하기 위해 NullValueHandling setting을 사용하고, System.Text.Json은 DefaultIgnoreCondition property를 WhenWritingNull로 설정한다. 

 

7. Allow numbers in quotes

Newtonsoft.Json은 serialize, deserialize할때, quotes로 감싼 숫자를 허용한다. 

System.Text.Json은 JsonSerializerOptions.NumberHandling을 WriteAsString으로 설정한다. 

 

System.Text.Json에 없는 기능들

  • Datatable, DBNull, TimeSpan과 같은 built-in type을 사용한다면, System.Text.Json은 지원하지 않는다. Newtonsoft.Json을 사용하는 것이 낫다. 
  • Polymorphic serialization, deserialization은 기본으로 제공하지 않지만, custom converter를 통해 구현 가능하다. 
  • DataMemberAttribute, IgnoreDataMemberAttribute와 같은 System.Runtime.Serialization namespace에서 나온 attribute를 지원하지 않는다. 
  • quotes가 없는 property, single quotes로 감싼 string, string property를 위한 non-string JSON value를 허용하지 않는다. 
  • System.Text.Json은 serialization하는 중, 순환참조가 있으면 에러를 throw한다. 참조를 유지하고 순환참조를 handle하려면 JsonSerializerOptions.ReferenceHandler를 Preserve로 세팅한다. 
    public class Employee
    {
        public string? Name { get; set; }
        public Employee? Manager { get; set; }
        public List<Employee>? DirectReports { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Employee tyler = new()
            {
                Name = "Tyler Stein"
            };

            Employee adrian = new()
            {
                Name = "Adrian King"
            };

            tyler.DirectReports = new List<Employee> { adrian };
            adrian.Manager = tyler;

            string tylerJson = System.Text.Json.JsonSerializer.Serialize(tyler);

        }
    }

에러내용 : System.Text.Json.JsonException: 'A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. 

 

Performance 비교

System.Text.Json이 Newtonsoft.Json보다 빠르다!

 

Migrate 할지 말지 

  • 이미 프로젝트에서 Newtonsoft.Json을 사용하고 있다면, 변경하는데 여러가지 장애가 있을 수 있다. DTO의 attributes등...
  • 새로운 project를 시작한다면, System.Text.Json을 사용하는 것 추천

 

728x90
반응형

댓글