2016-09-08 3 views
2

나는 일련의 도메인 객체를 가지고 있으며 다른 응용 프로그램으로 전송 한 다음 Json.Net을 사용하여 비 직렬화합니다. 이러한 개체는 속성을 가질 수있다 여러 파생 클래스에서 다른 종류로 $type 속성을 추가 내가 TypeNameHandling.Auto을 사용했습니다 클래스를 수정하지 않고 동적 속성을 생략 하시겠습니까?

  • 동적 속성과 추상 기본 클래스로 정의

    • 선언 된 형태. 그러나이 설정은 내 동적 속성에 원치 않는 부작용이 있습니다. 즉, 해당 유형이 선언되기도합니다.

      아래 예제에서 model은 내 C# 코드에서 public dynamic Model { get; set; }으로 정의 된 동적 속성입니다. 다른 어셈블리에서이 문자열을 역 직렬화하려고 할 때

      "model":{"$type":"<>f__AnonymousType0`3[[System.String, mscorlib],[System.String, mscorlib],[System.String, mscorlib]], ExampleAssembly","link":"http://www.google.com","name":"John"} 
      

      는 Json.Net는 (물론)을 ExampleAssembly를 찾을 수 없습니다. TypeNameHandling.None 속성을 사용하여 성공적으로 dynamic 직렬화 복원 할 수있는 다음과 같은 속성 직렬화

      "model": {"link":"http://www.google.com","name":"John"} 
      

      에게 제공합니다. 그러나 이렇게하면 파생 형식의 deserialization이 중단됩니다.

      사용자 정의 코드 IContractResolver 및 기타 사용자 정의 코드를 구현하지 않고이 기능을 작동시키는 방법에 대한 아이디어가 있으십니까?

      도메인 개체를 소유하고 있지 않으므로 속성을 사용하여 해당 속성을 등록하거나 인터페이스를 구현할 수 없습니다. 찾고 싶은 것은 형식을 생략하는 serializer의 일종의 설정입니다 dynamics.

      IMHO 이것은 설정을 통해 어떻게 든 구성 할 수 있어야합니다. 단지 찾지 못했습니다.

  • +0

    [JsonIgnore] 또는 조건부 직렬화 (http://www.newtonsoft.com 여기

    JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, ContractResolver = OmitTypeNamesOnDynamicsResolver.Instance }; string json = JsonConvert.SerializeObject(foo, settings); 

    의 개념을 증명하기 위해 왕복 데모 인 /json/help/html/conditionalproperties.htm) 유용 할 수도 있습니다. – Nair

    +0

    @Nair : 감사합니다.하지만 불행합니다.나는 도메인 객체를 소유하지 않으므로 속성이나 메소드로 장식 할 수 없다. 게다가, 그것은 모든 객체에 대해 이렇게해야하므로 잘 확장되지 않습니다. – pardahlman

    +0

    관련 속성에'[JsonProperty (TypeNameHandling = TypeNameHandling.None)]'을 추가하는 것이 가장 간단한 방법 일 것입니다. 그러나 주석에서 당신은 그렇게 할 수 없다고 말합니다. 해당 요구 사항을 명확히하기 위해 질문을 편집 할 수 있습니다. – dbc

    답변

    2

    Json.Net은 동적 유형에 대해서만 형식 이름 처리를 해제하는 특정 설정을 제공하지 않습니다. 관련 동적 속성을 [JsonProperty(TypeNameHandling = TypeNameHandling.None)]으로 표시 할 수 없다면 (또는 Json.Net 소스 코드 자체를 수정하지 않은 경우) 사용자 지정 계약 해결자를 구현하여 프로그래밍 방식으로 동작을 적용 할 수 있습니다 . 하지만 걱정하지 마세요. DefaultContractResolver 또는 CamelCasePropertyNamesContractResolver과 같은 Json.Net 제공 해결 프로그램 중 하나에서 해결자를 파생 시키면 어렵지 않습니다.

    using System; 
    using System.Reflection; 
    using Newtonsoft.Json; 
    using Newtonsoft.Json.Serialization; 
    
    public class OmitTypeNamesOnDynamicsResolver : DefaultContractResolver 
    { 
        public static readonly OmitTypeNamesOnDynamicsResolver Instance = new OmitTypeNamesOnDynamicsResolver(); 
    
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
        { 
         JsonProperty prop = base.CreateProperty(member, memberSerialization); 
         if (member.GetCustomAttribute<System.Runtime.CompilerServices.DynamicAttribute>() != null) 
         { 
          prop.TypeNameHandling = TypeNameHandling.None; 
         } 
         return prop; 
        } 
    } 
    

    그런 다음, 바로 JsonSerializerSettings에 리졸버를 추가하고 모든 설정해야한다 : 여기

    는 당신이 필요로하는 모든 코드입니다.

    public class Program 
    { 
        public static void Main(string[] args) 
        { 
         Foo foo = new Foo 
         { 
          Model = new { link = "http://www.google.com", name = "John" }, 
          Widget1 = new Doodad { Name = "Sprocket", Size = 10 }, 
          Widget2 = new Thingy { Name = "Coil", Strength = 5 } 
         }; 
    
         JsonSerializerSettings settings = new JsonSerializerSettings 
         { 
          TypeNameHandling = TypeNameHandling.Auto, 
          ContractResolver = OmitTypeNamesOnDynamicsResolver.Instance, 
          Formatting = Formatting.Indented 
         }; 
    
         string json = JsonConvert.SerializeObject(foo, settings); 
         Console.WriteLine(json); 
         Console.WriteLine(); 
    
         Foo foo2 = JsonConvert.DeserializeObject<Foo>(json, settings); 
         Console.WriteLine(foo2.Model.link); 
         Console.WriteLine(foo2.Model.name); 
         Console.WriteLine(foo2.Widget1.Name + " (" + foo2.Widget1.GetType().Name + ")"); 
         Console.WriteLine(foo2.Widget2.Name + " (" + foo2.Widget2.GetType().Name + ")"); 
        } 
    } 
    
    public class Foo 
    { 
        public dynamic Model { get; set; } 
        public AbstractWidget Widget1 { get; set; } 
        public AbstractWidget Widget2 { get; set; } 
    } 
    
    public class AbstractWidget 
    { 
        public string Name { get; set; } 
    } 
    
    public class Thingy : AbstractWidget 
    { 
        public int Strength { get; set; } 
    } 
    
    public class Doodad : AbstractWidget 
    { 
        public int Size { get; set; } 
    } 
    

    출력 :

    { 
        "Model": { 
        "link": "http://www.google.com", 
        "name": "John" 
        }, 
        "Widget1": { 
        "$type": "Doodad, JsonTest", 
        "Size": 10, 
        "Name": "Sprocket" 
        }, 
        "Widget2": { 
        "$type": "Thingy, JsonTest", 
        "Strength": 5, 
        "Name": "Coil" 
        } 
    } 
    
    http://www.google.com 
    John 
    Sprocket (Doodad) 
    Coil (Thingy) 
    
    +0

    Beautiful! 앞에서 말했듯이, 커스텀 클래스를 사용하지 않고 상자에서 벗어나는 기능을 사용하고 싶습니다. 그러나 귀하의 연락처 리졸버 구현은 매우 작았습니다. 잘 쓰여진 응답 주셔서 감사합니다! – pardahlman

    +0

    문제는 없습니다. 내가 도울 수있어서 기뻐. –

    관련 문제