2014-01-08 56 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(); 
      } 
     } 
    }