Newtonsoft.Json에서 Object Deserializing 하기
Package ver 13.0.3
공식 Documetation이 있기는 한데 정리가 하나도 안 되어 있다. 이해한 김에 이걸 까먹을 내가 언젠가 보려고 쓰는 글.
JsonConvert.Deserialize()
JToken.ToObject()
목적: 이 두 함수가 올바르게 작동하는 클래스 만들기.
Vector3
, Color
등
즉, 복잡한 기능이 없고 public
primitive field 몇 개를 저장하는 것이 역할의 다인 struct
다. 이 경우는 따로 조작이 필요하지 않고, primitive type처럼 작동한다. 이 글 내에 내가 primitive type을 언급한다면 이 친구들도 포함된다고 생각하면 된다.
Collections
List<T>
, T[]
등의 타입을 말하는 것이다. 상기한 단순한 struct 같은 것과는 거리가 멀지만 자주 쓰이는 친구들이다보니 패키지에서 T가 primitive인 등 간단한 경우에는 primitive type처럼 쓸 수 있도록 지원한다. [Serializing Collections]
private field가 있다면?
[JsonProperty]
를 붙이는 것으로 public
처럼 행동하게 한다. [JsonIgnore]
로 반대의 작업도 가능하다. 프로퍼티 등으로 인해 문제가 생길 수 있으니 잘 달아주자. [Serialization Attributes]
class
Vector3
문단과 마찬가지인 상황이라면 문제가 없다. 다만 그런 걸 class
로 만들었을 이유가 있을 테니 생성자 등에 부작용이 없는지 살펴보자. 기본적으로 Newtonsoft.Json은 인수가 없는 생성자를 불러서 생성자가 반환된 뒤에 JSON에 담긴 내용대로 field를 채운다. 생성자에 부작용이 있다면 잘못 전달된 기본값이 버그를 일으킬 수 있다.
생성자를 고르는 기준
[JsonConstructor]
가 붙은 생성자 > 인수가 없는 생성자 > 유일하게 존재하는 다른 생성자
[json.net - How does JSON deserialization in C# work - Stack Overflow]
→ 생성자에 인수가 있을 때에는, 인수의 이름이 중요하다. field와 이름이 같으면 넣어준다(대소문자는 상관 없다)
reference type field가 있을 때
reference로 cycle만 생기지 않으면 된다. node가 parent를 저장하지 않는 트리라던가, 그런 건 괜찮다. doubly linked list는 즉시 예외를 뱉을 것이다.
cycle까지 있을 때
JSON 최적화 등을 이유로 Serialize 로직을 커스텀하고 싶을 때
JsonConverter
를 구현해야 한다.
internal class RailJsonConverter : JsonConverter<RailSegment>
{
public override RailSegment? ReadJson(JsonReader reader, Type objectType, RailSegment? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
{
JObject root = (JObject)JToken.Load(reader);
return new(
root["curve"]?.ToObject<BezierCurve>(serializer) ?? new(new(0, 0, 0), new(1, 0, 0), new(2, 0, 0)),
root["color"]?.ToObject<Color>(serializer) ?? Color.DarkGray
);
}
public override void WriteJson(JsonWriter writer, RailSegment? value, Newtonsoft.Json.JsonSerializer serializer)
{
if (value == null) return;
new JObject()
{
{ "id", value.Id },
{ "curve", JToken.FromObject(value.Curve) },
{ "color", JToken.FromObject(value.Color) },
}.WriteTo(writer);
}
}
대충 이런 느낌으로 만들면 된다. 귀찮은 설명보다 이게 나을 것. 이 JsonConverter
객체를 Deserialize
에 인수로 전해 드리면 된다.