How do I parse a JSON string to a C# object using inheritance / polymorphism

∥☆過路亽.° 提交于 2019-12-11 07:50:06

问题


I want to parse a JSON string to a C# object that could be polymorph.

To sum it up: I don't want to instantiate the root object, but i want to instantiate the inherited object depending on the JSON input.

Here's an example of the C# objects I use:

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) {...}
}

And I have this JSON string that is an array containing two JSON objects:

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

I want to have this JSON-string deserialized as a C# object (list/array), each item should be instantiated as a subclass (Wheel or Break), but since List items have to be on the same denominator, the list type has to be of type Module.


回答1:


If you use the Newtonsoft JSON Library, you can create some custom converters, as follows:

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;
    }
}

You then pass an instance of this helper to Newtonsoft when deserializing:

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



回答2:


I don't think you can do it in one neat swoop. If i'd had to do it, i'd probably do it like this.

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)); 

}

And ReSharper then converted all that code in the foreach loop into this nifty little one-liner.

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();

Edith: You need http://james.newtonking.com/json for that.



来源:https://stackoverflow.com/questions/27311635/how-do-i-parse-a-json-string-to-a-c-sharp-object-using-inheritance-polymorphis

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!