如何使用Newtonsoft JSON.NET将JSON反序列化为IEnumerable <BaseType>


69

给出以下JSON:

[
  {
    "$id": "1",
    "$type": "MyAssembly.ClassA, MyAssembly",
    "Email": "me@here.com",
  },
  {
    "$id": "2",
    "$type": "MyAssembly.ClassB, MyAssembly",
    "Email": "me@here.com",
  }
]

这些类:

public abstract class BaseClass
{
    public string Email;
}
public class ClassA : BaseClass
{
}
public class ClassB : BaseClass
{
}

如何将JSON反序列化为:

IEnumerable<BaseClass> deserialized;

我不能使用,JsonConvert.Deserialize<IEnumerable<BaseClass>>()因为它抱怨BaseClass很抽象。

Answers:


91

你需要:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

string strJson = JsonConvert.SerializeObject(instance, settings);

因此,JSON如下所示:

{
  "$type": "System.Collections.Generic.List`1[[MyAssembly.BaseClass, MyAssembly]], mscorlib",
  "$values": [
    {
      "$id": "1",
      "$type": "MyAssembly.ClassA, MyAssembly",
      "Email": "me@here.com",
    },
    {
      "$id": "2",
      "$type": "MyAssembly.ClassB, MyAssembly",
      "Email": "me@here.com",
    }
  ]
}

然后,您可以反序列化它:

BaseClass obj = JsonConvert.DeserializeObject<BaseClass>(strJson, settings);

文档:TypeNameHandling设置


@AndrewBullock你知道参数$type是什么吗?我发现了很多例子,但没有解释。为什么需要先指定MyAssembly. ClassA然后再指定MyAssembly?链接的TypeNameHandling页面也没有提供任何关于此谜的解释:(
t3chb0t

当然,这里的最后一行不正确,您有一个List<BaseClass>试图将反序列化为a的问题,BaseClass 如果最后一行var obj = JsonConvert.DeserializeObject<List<BaseClass>>(strJson, settings)
不可读的话

是否可以"$type": "MyAssembly.ClassB, MyAssembly",在json中未包含时进行此工作, 并使用自定义条件来确定派生的对象类型以反序列化以使用其他条件?
Chinwobble

3
请注意,这会使
gjvdkamp

2
好的解决方案,但在应用之前,请先阅读此alphabot.com/security/blog/2017/net/…–
Abhinesh

27

这是一种无需在json中填充$ type的方法。

Json转换器:

public class FooConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(BaseFoo));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        if (jo["FooBarBuzz"].Value<string>() == "A")
            return jo.ToObject<AFoo>(serializer);

        if (jo["FooBarBuzz"].Value<string>() == "B")
            return jo.ToObject<BFoo>(serializer);

        return null;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

使用它:

var test = JsonConvert.DeserializeObject<List<BaseFoo>>(result, new JsonSerializerSettings() 
{ 
    Converters = { new FooConverter() }
});

这里


1
这个答案非常好,但是如果缺少“ FooBarBuzz”(即您需要格式一致的json),则会出错。您还可以检查领域存在如下所示- stackoverflow.com/a/19158793/852806
HockeyJ


0

您还可以将可枚举包装在一个类中:

class Wrapper
{
    IEnumerable<BaseClass> classes;
}

然后序列化和反序列化。


我和你有同样的问题,这对我不起作用(因为我已经在这样做了),当您反序列化它时,它也会出于同样的原因抱怨。
2014年

-1

因为我只需要一个用于特定基类的一侧序列化程序(即可使API返回派生类的属性),所以我想出了当前的解决方案

public class CustomConverter : JsonConverter<BaseClass>
{
    private readonly JsonSerializerOptions _serializerOptions;

    public CustomConverter()
    {
        _serializerOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            IgnoreNullValues = true,
        };
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(BaseClass));
    }

    public override BaseClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, BaseClass value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(JsonSerializer.SerializeToUtf8Bytes(value, value.GetType(), _serializerOptions));
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.