Json.net序列化/反序列化派生类型?

Wil*_*ill 84 c# serialization json json.net

json.net(newtonsoft)
我正在查看文档,但我找不到任何关于这个或最好的方法来做到这一点.

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);
Run Code Online (Sandbox Code Playgroud)

现在我在序列化列表中有Derived对象.如何反序列化列表并返回派生类型?

Mad*_*nyo 88

您必须启用类型名称处理并将其作为设置参数传递给(反)序列化程序.

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);
Run Code Online (Sandbox Code Playgroud)

这将导致派生类的正确反序列化.它的一个缺点是它将命名您正在使用的所有对象,因此它将命名您放置对象的列表.

  • +1.我谷歌搜索30分钟,直到我发现你需要使用相同的SerializeObject和DeserializeObject设置.我假设它会隐式使用$ type,如果它在反序列化时存在,傻我. (29认同)
  • `TypeNameHandling.Auto`也会这样做,并且更好,因为它在匹配字段/属性的类型时不会写实例类型名称,这通常是大多数字段/属性的情况. (20认同)
  • 当在另一个解决方案/项目上执行反序列化时,这不起作用。序列化时,解决方案的名称嵌入为类型:“SOLUTIONNAME.Models.Model”。在其他解决方案上进行反序列化时,它将抛出“JsonSerializationException:无法加载程序集'SOLUTIONNAME'。 (5认同)
  • 我从来不知道这会这么容易!我翻遍了谷歌,几乎要抓狂地试图找出如何编写自定义序列化器/反序列化器!然后我终于找到了这个答案,终于解决了!你是我的救星!感谢数万亿! (2认同)

kam*_*cus 39

如果您将类型存储在您的text(在本方案中应该如此),则可以使用JsonSerializerSettings.

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

但要小心.使用除此之外的任何东西都TypeNameHandling = TypeNameHandling.None可能导致自己陷入安全漏洞.

  • 您还可以使用`TypeNameHandling = TypeNameHandling.Auto` - 这将仅为声明类型(即`Base`)与实例类型(即`Derived`)不匹配的实例添加`$ type`属性.这样,它不会像`TypeNameHandling.All`那样膨胀你的JSON. (22认同)
  • 在公共端点上使用它时要小心,因为它会引发安全问题:https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-脆弱的web-API.html (3认同)
  • @gjvdkamp JEEZ 谢谢你,我不知道这个。将添加到我的帖子中。 (2认同)

rzi*_*ppo 14

由于这个问题非常普遍,因此如果您想控制类型属性名称及其值,添加一些操作可能会很有用。

很长的路要走,那就是编写custom JsonConverter来通过手动检查和设置type属性来处理(反序列化)。

一种更简单的方法是使用JsonSubTypes,它通过属性处理所有样板:

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

  • 我得到了需要,但我不喜欢让基类知道所有“KnownSubType”...... (4认同)
  • 如果您查看文档,还有其他选择。我只提供了我更喜欢的示例。 (2认同)
  • 这是更安全的方法,不会暴露您的服务以在反序列化时加载任意类型。 (2认同)
  • “JsonSubtypes” 是在哪里定义的?我使用的是 Newtonsoft.Json 版本 12.0.0.0,并且没有引用“JsonSubtypes”、“JsonSubTypes”或“JsonSubtypesConverterBuilder”(在那篇文章中提到)。 (2认同)

小智 8

使用这个JsonKnownTypes,它的使用方式非常相似,只是在 json 中添加了鉴别器:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}
Run Code Online (Sandbox Code Playgroud)

现在,当你序列化JSON对象将增加"$type""base""derived"价值,这将是使用了反序列化

序列化列表示例:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
Run Code Online (Sandbox Code Playgroud)