使用 Json.NET 将异构 JSON 数组反序列化为协变 List<>
Posted
技术标签:
【中文标题】使用 Json.NET 将异构 JSON 数组反序列化为协变 List<>【英文标题】:Deserializing heterogenous JSON array into covariant List<> using Json.NET 【发布时间】:2012-01-04 16:45:07 【问题描述】:我有一个 JSON 数组,其中包含具有不同属性的不同类型的对象。其中一个属性称为“类型”,它确定数组项的类型。这是我的数据示例:
[
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"
]
]
]
为了将这些对象映射到我的 .Net 实现,我定义了一组类:一个基类和几个子类(具有复杂的层次结构,具有 4 个“代”)。这里只是这些类的一个小例子:
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;
我想要达到的是能够将这个数组反序列化为一个协变的泛型列表,声明为:
List<TExpression> myexpressions = JsonConvert.DeserializeObject<List<TExpression>>(aststring);
此列表应包含从 TExpression 继承的适当子类的实例,因此我可以稍后在我的代码中使用以下代码:
foreach(TExpression t in myexpressions)
if (t is TComment) dosomething;
if (t is TTokenSequence) dosomethingelse;
如何使用 JSON.NET 访问它?
【问题讨论】:
***.com/questions/35182949/…的可能重复 【参考方案1】:这是一个使用 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());
【讨论】:
不会在抽象Create
方法的主体中抛出 NotImplementedException
导致问题吗?【参考方案2】:
也可以用我的JsonSubTypes库以声明的方式实现它:
[JsonConverter(typeof(JsonSubtypes), "valueType")]
[JsonSubtypes.KnownSubType(typeof(IntItem), "int")]
[JsonSubtypes.KnownSubType(typeof(StringItem), "string")]
public abstract class Item
public string ValueType get; set;
[JsonProperty("valueTypeId")]
public int ValueTypeId get; set;
[JsonProperty("name")]
public string Name get; set;
public override 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;
[TestMethod]
public void Demo()
// 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\"]";
var result = JsonConvert.DeserializeObject<List<Item>>(json);
Assert.AreEqual("IntItem object ValueType=int, Value=5", result[0].ToString());
Assert.AreEqual("StringItem object ValueType=string, Value=some thing; Num Chars= 11", result[1].ToString());
Assert.AreEqual("IntItem object ValueType=int, Value=2", result[2].ToString());
【讨论】:
【参考方案3】:CustomCreationConverter 应该能够处理这个问题。
【讨论】:
以上是关于使用 Json.NET 将异构 JSON 数组反序列化为协变 List<>的主要内容,如果未能解决你的问题,请参考以下文章
使用来自 URL 的 JSON.NET 反序列化 JSON 数组
如何使用 json.net 将 json 数组添加到 JObject 的属性中