Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

c# - Deserializing heterogenous JSON array into covariant List<> using Json.NET

I have a JSON-array containing objects of different types with different properties. One of the properties is called "type" and determines the type of the array item. Here is an example of my data:

   [{
        type : "comment",
        text : "xxxx"
    }, {
        type : "code",
        tokens : [{
                type : "ref",
                data : "m"
            }, {
                type : "operator",
                data : "e"
            }
        ]
    }, {
        type : "for",
        boundLocal : {
            type : "local",
            name : "i",
            kind : "Number"
        },
        upperBound : {
            type : "ref",
            tokens : [{
                    type : "operator",
                    data : "3"
                }, {
                    type : "operator",
                    data : "0"
                }
            ]
        },
        body : [{
                type : "code",
                tokens : [{
                        type : "ref",
                        data : "x"
                    }
                ]
            }, {
                type : "code",
                tokens : [{
                        type : "ref",
                        data : "y"
                    }
                }
                ]
        ]
    ]

To map those objects to my .Net implementation I define a set of classes: one base class and several child classes (with a complex hierarchy, having 4 "generations"). Here is just a small example of these classes:

public abstract class TExpression
{
    [JsonProperty("type")]
    public string Type { get; set; }
}

public class TComment : TExpression
{
    [JsonProperty("text")]
    public string Text { get; set; }
}   

public class TTokenSequence : TExpression
{
    [JsonProperty("tokens")]
    public List<TToken> Tokens { get; set; }
}

What I want to reach is to be able to deserialize this array into a covariant generic list, declared as:

List<TExpression> myexpressions = JsonConvert.DeserializeObject<List<TExpression>>(aststring);

This list should contain the instances of appropriate child classes inheriting from TExpression, so I can use the following code later in my code:

foreach(TExpression t in myexpressions)
{
    if (t is TComment) dosomething;
    if (t is TTokenSequence) dosomethingelse;
}

How can I reach it using JSON.NET?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Here is an example using CustomCreationConverter.

public class JsonItemConverter :  Newtonsoft.Json.Converters.CustomCreationConverter<Item>
{
    public override Item Create(Type objectType)
    {
        throw new NotImplementedException();
    }

    public Item Create(Type objectType, JObject jObject)
    {
        var type = (string)jObject.Property("valueType");
        switch (type)
        {
            case "int":
                return new IntItem();
            case "string":
                return new StringItem();
        }

        throw new ApplicationException(String.Format("The given vehicle type {0} is not supported!", type));
    }

    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
        var target = Create(objectType, jObject);

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

        return target;
    }
}

public abstract class Item
{
    public string ValueType { get; set; }

    [JsonProperty("valueTypeId")]
    public int ValueTypeId { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    public new virtual string ToString() { return "Base object, we dont' want base created ValueType=" + this.ValueType + "; " + "name: " + Name; }
}

public class StringItem : Item
{
    [JsonProperty("value")]
    public string Value { get; set; }

    [JsonProperty("numberChars")]
    public int NumberCharacters { get; set; }

    public override string ToString() { return "StringItem object ValueType=" + this.ValueType + ", Value=" + this.Value + "; " + "Num Chars= " + NumberCharacters; }

}

public class IntItem : Item
{
    [JsonProperty("value")]
    public int Value { get; set; }

    public override string ToString() { return "IntItem object ValueType=" + this.ValueType + ", Value=" + this.Value; }
}

class Program
{
    static void Main(string[] args)
    {
        // json string
        var json = "[{"value":5,"valueType":"int","valueTypeId":1,"name":"numberOfDups"},{"value":"some thing","valueType":"string","valueTypeId":1,"name":"a","numberChars":11},{"value":2,"valueType":"int","valueTypeId":2,"name":"b"}]";

        // The above is deserialized into a list of Items, instead of a hetrogenous list of
        // IntItem and StringItem
        var result = JsonConvert.DeserializeObject<List<Item>>(json, new JsonItemConverter());

        foreach (var r in result)
        {
            // r is an instance of Item not StringItem or IntItem
            Console.WriteLine("got " + r.ToString());
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...