2017-12-14 10 views
2

세 가지 수준으로 이동하여 생성 된 몇 가지 .NET 클래스가 있고 특별한 형식으로 serialize하려는. 그래서 Newtonsoft.Json을 사용하여 사용자 지정 Json Serializer를 작성하기 시작했습니다.깊은 중첩 된 개체에 대한 사용자 지정 Json Serializer

내가 완전히 설명하기 어려울 것으로 판단, 그래서 여기에 목표와 함께 코드를 게시 한 : 기본적으로 https://dotnetfiddle.net/CDGcMW

를, 객체를 포함 할 것이며, 그 속성이있을 것 초기 배열이있다 목적. 어려운 부분은 이러한 속성을 알 수 없다는 것입니다. 따라서 사용자 지정 serializer를 만들려고합니다.

내가 만든 Json을 여기에서 만들 수있는 방법을 결정하는 데 도움이된다면 https://dotnetfiddle.net/CDGcMW은 "목표"JSON이되어 크게 향상 될 것입니다.

편집 : 더 작은 예제로 업데이트되었습니다. 원래는 여기에 있습니다 : https://dotnetfiddle.net/dprfDu

+0

정말 잘 모르겠습니다. 객체 그래프에 깊게 중첩 될 때 유형의 속성을 제외 시키려고하지만 그래프 상단에있는 경우는 제외합니다. 그렇다면 아마도 [Json.NET은 깊이와 속성에 의해 직렬화됩니다] (https://stackoverflow.com/q/36159424/3744182) 도움이 될 것입니다. – dbc

+0

그 링크가 도움이되지 않는다면 가능한 한 적은 외부 필드와 속성으로 현재 출력과 필요한 출력을 보여주는 [mcve]로 피들을 단순화 할 수있는 방법이 있습니까? – dbc

+0

감사합니다. 예제를 더 짧게 업데이트했으며 IContractResolver를 살펴 보겠습니다. – ajtatum

답변

3

당신의 "목표"JSON은 SubDataMappers 목록의 치료는 아이들이 null이 아닌 DataMapperProperty 또는 SubDataMappers의 비어 있지 않은 목록을 가지고 있는지 여부에 따라 다르기 때문에 처리하기 까다 롭습니다. 전자의 경우 자식 당 하나의 속성을 포함하는 객체로 렌더링하려고합니다. DataMapper; 후자는 각각 DataMapper을 포함하는 객체의 배열입니다. 또한 잘 알려진 속성의 값이 아닌 DataMapperName 속성을 JSON의 키로 사용하고 있습니다. 이 두 가지 제약 조건을 감안할 때 최선의 공격 계획은 목록 (DataMappers)에서 작동하는 JsonConverter을 단일 인스턴스로 만드는 것이라고 생각합니다. 그렇지 않으면 컨버터 코드가 꽤 엉망이 될 것입니다.

public class DataMapperListConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(List<DataMapper>); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     List<DataMapper> list = (List<DataMapper>)value; 
     if (list.Any(dm => dm.DataMapperProperty != null)) 
     { 
      JObject obj = new JObject(list.Select(dm => 
      { 
       JToken val; 
       if (dm.DataMapperProperty != null) 
        val = JToken.FromObject(dm.DataMapperProperty, serializer); 
       else 
        val = JToken.FromObject(dm.SubDataMappers, serializer); 
       return new JProperty(dm.Name, val); 
      })); 
      obj.WriteTo(writer); 
     } 
     else 
     { 
      serializer.Serialize(writer, 
       list.Select(dm => new Dictionary<string, List<DataMapper>> 
       { 
        { dm.Name, dm.SubDataMappers } 
       })); 
     } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JToken token = JToken.Load(reader); 
     if (token.Type == JTokenType.Object) 
     { 
      return token.Children<JProperty>() 
       .Select(jp => 
       { 
        DataMapper mapper = new DataMapper { Name = jp.Name }; 
        JToken val = jp.Value; 
        if (val["data-type"] != null) 
         mapper.DataMapperProperty = jp.Value.ToObject<DataMapperProperty>(serializer); 
        else 
         mapper.SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer); 
        return mapper; 
       }) 
       .ToList(); 
     } 
     else if (token.Type == JTokenType.Array) 
     { 
      return token.Children<JObject>() 
       .SelectMany(jo => jo.Properties()) 
       .Select(jp => new DataMapper 
       { 
        Name = jp.Name, 
        SubDataMappers = jp.Value.ToObject<List<DataMapper>>(serializer) 
       }) 
       .ToList(); 
     } 
     else 
     { 
      throw new JsonException("Unexpected token type: " + token.Type.ToString()); 
     } 
    } 
} 

가정 : 그 허용하는 경우, 다음 컨버터는 당신이 원하는 무엇을 제공해야

  • 당신은 결코 그 자체로 하나의 DataMapper를 직렬화되지 않습니다; 그것은 항상 목록에 포함됩니다.
  • DataMappers은 임의의 깊이로 중첩 될 수 있습니다.
  • DataMapper은 항상 각 레벨에서 고유 한 Name이 아닌 null이됩니다.
  • DataMapper은 0이 아닌 DataMapperProperty과 비어 있지 않은 목록 인 SubDataMappers을 가질 수 없습니다.
  • DataMapperProperty은 항상 null이 아닙니다 DataType입니다.
  • DataMapperNamedata-type이되지 않습니다.

마지막 4 가지 가정이 사실이 아니라면,이 JSON 형식은 수행하려는 작업에 적합하지 않으므로 다시 생각해 봐야합니다.

변환기를 사용하려면 다음과 같이 변환기를 serializer 설정에 추가해야합니다. serialize 및 deserialize 할 때 설정을 사용하십시오. DataMapper 클래스에서 [JsonConverter] 특성을 제거하십시오.https://dotnetfiddle.net/8KycXB

+0

대단히 감사합니다. 감사합니다! 추가 개발이 끝나면 원래 수준이 아닌 3 단계 수준의 개체가있는 시나리오가 있습니다. 나는 당신의 코드를 포크하고 객체가 null 인 결과를 가져 오는 3 단계 깊이의 객체를 포함하도록 조정했다. 다음은 코드입니다 : https://dotnetfiddle.net/AFl36o. 너무 많이 다시 고마워, 많이 배우고있어! – ajtatum

+0

이것은 내가 가진 WriteJson 함수로, 솔루션보다 훨씬 복잡하며 ReadJson이 작동하지 않습니다. https://dotnetfiddle.net/fAKOrK. 세 가지 레벨을 지원하는 솔루션을 만드는 데 도움이된다면 놀라운 것이 될 것입니다! – ajtatum

+0

레벨 3이 Null로 나옵니다. 이제 Level 2에있는 SubDataMappers의 이기종 목록이 있습니다. 두 개의 항목에 DataMapperProperties가 있고 새 항목에는 SubMappers 목록이 있습니다. 내 변환기는 목록을 배열 또는 객체로 렌더링할지 여부를 결정하는 수단으로 의존하기 때문에 다른 하나 또는 모두를 기대하고있었습니다. serialization 형식을 따르도록 변환기를 수정할 수는 있지만 비 직렬화에서 새로운 문제가 발생합니다. (계속) –

0

당신은 ExpandoObject로 모든 입력 클래스를 대체하여 JSON.NET와 깊은 중첩 직렬화를 얻을 수 있습니다 여기에

var settings = new JsonSerializerSettings() 
{ 
    Converters = new List<JsonConverter> { new DataMapperListConverter() }, 
    Formatting = Formatting.Indented 
}; 

은 왕복 데모입니다. 그것은 나를 위해 작동합니다. 그것이 당신을 위해 작동하는지 또는 당신을 보여주기 위해 샘플이 필요하면 알려주십시오.

UPDATE : 여기

https://dotnetfiddle.net/jtebDs

희망이 당신이 출력을 볼 싶은 것이있는 작업 예제를합니다. 궁금한 점이 있으면 알려주세요.

+0

@ brian-rogers 솔루션 또는 그와 다른 작업을 하시겠습니까? 필자는 WriteJson 함수를 생성하기 위해 https://dotnetfiddle.net/fAKOrK라는 코드를 작성하여 작동시킬 수있었습니다. ReadJson 함수를 만드는 것은 어렵다고 증명됩니다. 예제/샘플을 향해 나를 가리킬 수 있다면 그것을 고맙게 생각합니다. – ajtatum

+0

위의 작업 샘플을 업데이트 섹션에 게시하십시오. – RajN