2015-04-09 16 views
8

Ana sınıfın bir polimorfik çocuk nesnesinin örneğine sahip olmasıyla, json'dan/C# sınıflarına serileştirme/serileştirme yapabilmekteyiz. Bunu yapmak Json.Net'in TypeNameHandling.Auto ayarını kullanarak kolaydır. Ancak, "$ type" alanı olmadan bunu yapmak isteriz.Json.Net Polimorfik Çocuk Nesnesiyle Türün Serileştirilmesi

İlk düşünce, "$ türünü", bizim seçtiğimiz bir değere yeniden adlandırabilmemiz ve türün değerini alt türlerin düzgün şekilde eşleştireceği bir enum olması. Bunu bir seçenek olarak görmedim, ancak mümkün olup olmadığını öğrenmekten memnun olurum.

İkinci düşünce aşağıdaki satırlarda oldu ... Aşağıda, üst düzey sınıfın, alt nesnede (SubTypeData) ne tür veriler içerdiği konusunda bir gösterge (SubTypeType) olan ilk sınıf geçiştir. Json.Net belgelerine bir miktar kazdım ve birkaç şey denedim ama hiç şansım olmadı.

Şu anda veri tanımı üzerinde tam denetime sahibiz, ancak dağıtıldıktan sonra işler kilitleniyor.

public class MainClass 
{ 
    public SubType   SubTypeType { get; set; } 
    public SubTypeClassBase SubTypeData { get; set; } 
} 

public class SubTypeClassBase 
{ 
} 

public class SubTypeClass1 : SubTypeClassBase 
{ 
    public string AaaField { get; set; } 
} 

public class SubTypeClass2 : SubTypeClassBase 
{ 
    public string ZzzField { get; set; } 
} 

cevap

7

kap sınıfında alt tip bilgi sahibi iki nedenden dolayı sorunludur: Json.NET ihtiva sınıfı okurken

  1. konteyner sınıfı örneği değil erişilebilir.
  2. Daha sonra, SubTypeClassBase özelliğini bir listeye dönüştürmeniz gerekirse, alt listeyi yerleştirecek bir yer yoktur.

Bunun yerine, temel sınıf bir özellik olarak alt tür bilgileri ekleme öneriyoruz: SubTypeClassBase için atanabilir bir nesne seri hale getirilmiş her

[JsonConverter(typeof(SubTypeClassConverter))] 
public class SubTypeClassBase 
{ 
    [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value 
    public SubType Type { get { return typeToSubType[GetType()]; } } 
} 

Artık özel alt tip enum tefrika olacak. Bunu yaptıktan sonra, seri hale getirme için, SubTypeClassBase için verilen bir SubTypeClassBase için json'u JObject'a yükleyen bir JsonConverter oluşturabilir, özelliğinin değerini kontrol eder ve JSON nesnesini uygun sınıf olarak serialterleştirir. Aşağıda

Prototip uygulaması:

public enum SubType 
{ 
    BaseType, 
    Type1, 
    Type2, 
} 

[JsonConverter(typeof(SubTypeClassConverter))] 
public class SubTypeClassBase 
{ 
    static readonly Dictionary<Type, SubType> typeToSubType; 
    static readonly Dictionary<SubType, Type> subTypeToType; 

    static SubTypeClassBase() 
    { 
     typeToSubType = new Dictionary<Type,SubType>() 
     { 
      { typeof(SubTypeClassBase), SubType.BaseType }, 
      { typeof(SubTypeClass1), SubType.Type1 }, 
      { typeof(SubTypeClass2), SubType.Type2 }, 
     }; 
     subTypeToType = typeToSubType.ToDictionary(pair => pair.Value, pair => pair.Key); 
    } 

    public static Type GetType(SubType subType) 
    { 
     return subTypeToType[subType]; 
    } 

    [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value 
    public SubType Type { get { return typeToSubType[GetType()]; } } 
} 

public class SubTypeClass1 : SubTypeClassBase 
{ 
    public string AaaField { get; set; } 
} 

public class SubTypeClass2 : SubTypeClassBase 
{ 
    public string ZzzField { get; set; } 
} 

public class SubTypeClassConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(SubTypeClassBase); 
    } 

    public override bool CanWrite { get { return false; } } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var token = JToken.Load(reader); 
     var typeToken = token["Type"]; 
     if (typeToken == null) 
      throw new InvalidOperationException("invalid object"); 
     var actualType = SubTypeClassBase.GetType(typeToken.ToObject<SubType>(serializer)); 
     if (existingValue == null || existingValue.GetType() != actualType) 
     { 
      var contract = serializer.ContractResolver.ResolveContract(actualType); 
      existingValue = contract.DefaultCreator(); 
     } 
     using (var subReader = token.CreateReader()) 
     { 
      // Using "populate" avoids infinite recursion. 
      serializer.Populate(subReader, existingValue); 
     } 
     return existingValue; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 
+0

ben yapmaya çalıştığım tam olarak ne bu! – GaTechThomas

+0

Teşekkür ederiz! Sonsuz yineleme kısmı beni öldürüyordu! –

1

Sen numaralandırma değerleri ile tip eşleştirme kayıt desteği JsonSubtypes dönüştürücü uygulaması ile deneyebilirsiniz. Senin durumunda

o şuna benzer:

 public class MainClass 
     { 
      public SubTypeClassBase SubTypeData { get; set; } 
     } 

     [JsonConverter(typeof(JsonSubtypes), "SubTypeType")] 
     [JsonSubtypes.KnownSubType(typeof(SubTypeClass1), SubType.WithAaaField)] 
     [JsonSubtypes.KnownSubType(typeof(SubTypeClass2), SubType.WithZzzField)] 
     public class SubTypeClassBase 
     { 
      public SubType SubTypeType { get; set; } 
     } 

     public class SubTypeClass1 : SubTypeClassBase 
     { 
      public string AaaField { get; set; } 
     } 

     public class SubTypeClass2 : SubTypeClassBase 
     { 
      public string ZzzField { get; set; } 
     } 

     public enum SubType 
     { 
      WithAaaField, 
      WithZzzField 
     } 

     [TestMethod] 
     public void Deserialize() 
     { 
      var obj = JsonConvert.DeserializeObject<MainClass>("{\"SubTypeData\":{\"ZzzField\":\"zzz\",\"SubTypeType\":1}}"); 
      Assert.AreEqual("zzz", (obj.SubTypeData as SubTypeClass2)?.ZzzField); 
     }