2014-12-05 73 views
1

我想将JSON字符串解析为可能是多态的C#对象。总结一下:我不想实例化根对象,但是我想根据JSON输入实例化继承的对象。如何使用继承/多态解析JSON字符串到C#对象

这是我使用C#对象的示例:

public class Module { 
    public string name; 
} 
public class Wheel : Module { 
    public int amount; 
    public Wheel(string name, int amount) : base(name) {...} 
} 
public class Break : Module { 
    public double delay; 
    public Break(string name, double delay) : base(name) {...} 
} 

而且我有这个JSON字符串,它是包含两个JSON对象的数组:

[{ 
    "name":"Wheel", 
    "amount":4 
},{ 
    "name":"Break", 
    "delay":1.0 
}] 

我想有这个JSON-字符串反序列化为C#对象(列表/数组),每个项目都应该实例化为子类(WheelBreak),但由于List项必须位于同一分母上,因此列表类型必须为Module

+0

之间有什么区别[你的老问题](http://stackoverflow.com/questions/27272202/litjson-parsing-inherited-object-in-c-sharp)和这个问题? – 2014-12-05 08:30:37

+0

更清晰的描述,旧的已被删除(或至少正在进行中) – marius 2014-12-05 08:35:18

+0

您是否试过Newtonsoft库? – Kutyel 2014-12-05 09:57:34

回答

4

如果使用Newtonsoft JSON Library,你可以创建一些自定义的转换器,如下:

public class ModuleObjectConverter : JsonCreationConverter<Module> 
{ 
    protected override Module Create(Type objectType, JObject jObject) 
    { 
     //This is the important part - we can query what json properties are present 
     //to figure out what type of object to construct and populate 
     if (FieldExists("amount", jObject)) { 
      return new Wheel(); 
     } else if (FieldExists("delay", jObject)) { 
      return new Break(); 
     } else { 
      return null; 
     } 
    } 

    private bool FieldExists(string fieldName, JObject jObject) 
    { 
     return jObject[fieldName] != null; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     //We don't deal with writing json, generally newtonsoft would make a good job of 
     //serializing these type of objects without having to use a custom writer anyway 
    } 
} 

//Generic converter class - could combine with above class if you're only dealing 
//with one inheritance chain, but this way it's reusable 
public abstract class JsonCreationConverter<T> : JsonConverter 
{ 
    protected abstract T Create(Type objectType, JObject jObject); 

    public override bool CanConvert(Type objectType) 
    { 
     return typeof(T).IsAssignableFrom(objectType); 
    } 
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     // Load JObject from stream 
     JObject jObject = JObject.Load(reader); 

     // Create target object based on JObject 
     T target = Create(objectType, jObject); 

     // Populate the object properties 
     serializer.Populate(jObject.CreateReader(), target); 

     return target; 
    } 
} 

然后,您可以通过这个帮手Newtonsoft的实例反序列化时:

var modules = JsonConvert.DeserializeObject<List<Module>>(jsonString, new ModuleObjectConverter()); 
0

我不认为你可以做到一个整洁的一举。如果我必须这样做,我可能会这样做。

var json = @" 
[{ 
""name"":""Wheel"", 
""amount"":4 
},{ 
""name"":""Break"", 
""delay"":1.0 
}]"; 

// get a list of possible types from the assembly containing Module. 
// don't know of a better way of doing this. 
var types = typeof (Module).Assembly.GetTypes(); 

// parse the original JSON into an array. 
var joList = JArray.Parse(json); 

// list I want to populate 
var listModule = new List<Module>(); 

foreach (dynamic token in joList) 
{ 
    string name = token.name; 

    // get the actual type. 
    var type = types.FirstOrDefault(x=>x.Name == name); 

    // if type is not found then continue. 
    if (type == null) 
     continue; 

    // if type is not a subclass of Module, continue. 
    if (!type.IsSubclassOf(typeof(Module))) 
     continue; 

    // now deserialize that token into the actual type and add it to the list 
    listModule.Add(JsonConvert.DeserializeObject(token.ToString(), type)); 

} 

然后ReSharper将foreach循环中的所有代码转换成这个漂亮的小单行程序。

var listModule = (from dynamic token in joList 
    let name = token.name 
    let type = types.FirstOrDefault(x => x.Name == (string) name) 
    where type != null 
    where type.IsSubclassOf(typeof (Module)) 
    select JsonConvert.DeserializeObject(token.ToString(), type)).Cast<Module>().ToList(); 

伊迪丝:需要http://james.newtonking.com/json了点。