2014-01-08 90 views
11

我想反序列化一個JSON字符串到一個具體的類,它從一個抽象類繼承,但我不能得到它的工作。我搜索了一些並嘗試了一些解決方案,但他們似乎也沒有工作。反序列化JSON到抽象類

這是我現在有:

abstract class AbstractClass { } 

class ConcreteClass { } 

public AbstractClass Decode(string jsonString) 
{ 
    JsonSerializerSettings jss = new JsonSerializerSettings(); 
    jss.TypeNameHandling = TypeNameHandling.All; 
    return (AbstractClass)JsonConvert.DeserializeObject(jsonString, null, jss); 
} 

但是,如果我嘗試投得到的對象,這是行不通的。

我不使用DeserializeObject的原因是我有許多具體類

有什麼建議嗎?

  • 我使用Newtonsoft.Json
+0

反序列化,你需要創建對象實例,但你不能創建抽象類 – Grundy

+0

的情況下,我希望它創建一個具體的類的實例,對不起,如果不清楚 – aochagavia

+0

你能提供多一點的代碼嗎? – Grundy

回答

8

嘗試這樣的事情

public AbstractClass Decode(string jsonString) 
{ 
    var jss = new JavaScriptSerializer(); 
    return jss.Deserialize<ConcreteClass>(jsonString); 
} 

UPDATE
對於這種情況,只要你想記錯的所有工作

public abstract class Base 
{ 
    public abstract int GetInt(); 
} 
public class Der:Base 
{ 
    int g = 5; 
    public override int GetInt() 
    { 
     return g+2; 
    } 
} 
public class Der2 : Base 
{ 
    int i = 10; 
    public override int GetInt() 
    { 
     return i+17; 
    } 
} 

.... 

var jset = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; 
Base b = new Der() 
string json = JsonConvert.SerializeObject(b, jset); 
.... 

Base c = (Base)JsonConvert.DeserializeObject(json, jset); 

哪裏c類型是test.Base {test.Der}

UPDATE

@Gusman建議使用TypeNameHandling.Objects代替TypeNameHandling.All。這足夠了,它會產生一個不太冗長的序列化。

+1

問題是我有很多具體的類,所以我沒有知道使用哪一個 – aochagavia

+0

@aochagavia所以讓你的抽象類不抽象和使用它:-) – Grundy

+2

它具有抽象方法,這就是爲什麼它是抽象的......我只是把更多的信息放在JSON上,比如classType或類似的東西。 – aochagavia

26

可能不想使用TypeNameHandling(因爲我們想要更緊湊的json或者希望使用除「$ type」之外的類型變量的特定名稱)。與此同時,customerCreationConverter approach不會工作,如果有人想將基類反序列化爲多個派生類中的任何一個,而不知道預先使用哪一個類。

另一種方法是在基類中使用int或其他類型並定義JsonConverter。

[JsonConverter(typeof(BaseConverter))] 
abstract class Base 
{ 
    public int ObjType { get; set; } 
    public int Id { get; set; } 
} 

class DerivedType1 : Base 
{ 
    public string Foo { get; set; } 
} 

class DerivedType2 : Base 
{ 
    public string Bar { get; set; } 
} 

基類的JsonConverter可以根據其類型反序列化對象。複雜的是,爲了避免堆棧溢出(JsonConverter重複調用它自己),必須在此反序列化過程中使用自定義合約解析器。

public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver 
{ 
    protected override JsonConverter ResolveContractConverter(Type objectType) 
    { 
     if (typeof(Base).IsAssignableFrom(objectType) && !objectType.IsAbstract) 
      return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow) 
     return base.ResolveContractConverter(objectType); 
    } 
} 

public class BaseConverter : JsonConverter 
{ 
    static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() }; 

    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(Base)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject jo = JObject.Load(reader); 
     switch (jo["ObjType"].Value<int>()) 
     { 
      case 1: 
       return JsonConvert.DeserializeObject<DerivedType1>(jo.ToString(), SpecifiedSubclassConversion); 
      case 2: 
       return JsonConvert.DeserializeObject<DerivedType2>(jo.ToString(), SpecifiedSubclassConversion); 
      default: 
       throw new Exception(); 
     } 
     throw new NotImplementedException(); 
    } 

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); // won't be called because CanWrite returns false 
    } 
} 

就是這樣。現在你可以使用序列化/反序列化任何派生類。你也可以用在其他類的基類和序列化/反序列化那些沒有任何額外的工作:

class Holder 
    { 
     public List<Base> Objects { get; set; } 
    } 
string json = @" 
     [ 
      { 
       ""Objects"" : 
       [ 
        { ""ObjType"": 1, ""Id"" : 1, ""Foo"" : ""One"" }, 
        { ""ObjType"": 1, ""Id"" : 2, ""Foo"" : ""Two"" }, 
       ] 
      }, 
      { 
       ""Objects"" : 
       [ 
        { ""ObjType"": 2, ""Id"" : 3, ""Bar"" : ""Three"" }, 
        { ""ObjType"": 2, ""Id"" : 4, ""Bar"" : ""Four"" }, 
       ] 
      }, 
     ]"; 

      List<Holder> list = JsonConvert.DeserializeObject<List<Holder>>(json); 
      string serializedAgain = JsonConvert.SerializeObject(list); 
      Debug.WriteLine(serializedAgain); 
+0

這是一個很好的解決方案;我會建議的唯一改進是,不是調用'return JsonConvert.DeserializeObject (jo.ToString(),SpecifiedSubclassConversion);',而是使用傳入的串行器'Return jo.ToObject(t,serializer);' –

+0

那麼你會得到stackoverflow例外 –

+0

這絕對是美妙的!非常感謝你的代碼! –

0
public class CustomConverter : JsonConverter 
{ 
    private static readonly JsonSerializer Serializer = new JsonSerializer(); 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var jObject = JObject.Load(reader); 
     var typeString = jObject.Value<string>("Kind"); //Kind is a property in json , from which we know type of child classes 
     var requiredType = RecoverType(typeString); 

     return Serializer.Deserialize(jObject.CreateReader(), requiredType); 
    } 

    private Type RecoverType(string typeString) 
    { 
     if (typeString.Equals(type of child class1, StringComparison.OrdinalIgnoreCase)) 
      return typeof(childclass1); 
     if (typeString.Equals(type of child class2, StringComparison.OrdinalIgnoreCase)) 
      return typeof(childclass2);    

     throw new ArgumentException("Unrecognized type"); 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return typeof(Base class).IsAssignableFrom(objectType) || typeof((Base class) == objectType; 
    } 

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

現在JsonSerializerSettings添加該轉換器如下

var jsonSerializerSettings = new JsonSerializerSettings(); 
     jsonSerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); 
     jsonSerializerSettings.Converters.Add(new CustomConverter()); 

加入後序列化或反序列化基類對象,如下所示

JsonConvert.DeserializeObject<Type>("json string", jsonSerializerSettings); 
0

我建議以下列方式使用CustomCreationConverter:

public enum ClassDiscriminatorEnum 
    { 
     ChildClass1, 
     ChildClass2 
    } 

    public abstract class BaseClass 
    { 
     public abstract ClassDiscriminatorEnum Type { get; } 
    } 

    public class Child1 : BaseClass 
    { 
     public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass1; 
     public int ExtraProperty1 { get; set; } 
    } 

    public class Child2 : BaseClass 
    { 
     public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass2; 
    } 

    public class BaseClassConverter : CustomCreationConverter<BaseClass> 
    { 
     private ClassDiscriminatorEnum _currentObjectType; 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      var jobj = JObject.ReadFrom(reader); 
      _currentObjectType = jobj["Type"].ToObject<ClassDiscriminatorEnum>(); 
      return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer); 
     } 

     public override BaseClass Create(Type objectType) 
     { 
      switch (_currentObjectType) 
      { 
       case ClassDiscriminatorEnum.ChildClass1: 
        return new Child1(); 
       case ClassDiscriminatorEnum.ChildClass2: 
        return new Child2(); 
       default: 
        throw new NotImplementedException(); 
      } 
     } 
    }