【问题标题】:Why does Json.NET not include $type for the root object when TypeNameHandling is Auto?当 TypeNameHandling 为 Auto 时,为什么 Json.NET 不包含根对象的 $type?
【发布时间】:2016-08-09 19:36:20
【问题描述】:

当我将 Json.NET 设置为序列化并将 TypeNameHandling 设置为 TypeNameHandling.Auto 时,它会为对象的子属性正确设置 $type,但不会为正在序列化的根对象这样做。为什么?

请考虑以下重现:

public class Animal
{
    public Animal[] Offspring { get; set; }
}

public class Dog : Animal {}

Animal fido = new Dog
{
    Offspring = new Animal[] { new Dog() }
};

var json = JsonConvert.SerializeObject(fido, 
    new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto
    });

发送到json 变量的Json 是:

{
    "Offspring": [{
        "$type": "MyApp.Dog, MyApp",
        "Offspring": null
    }]
}

Json.NET Documentation 表示 TypeNameHandling.Auto 的行为是:

当被序列化的对象的类型与其声明的类型不同时,包括 .NET 类型名称。

我的问题是 - 为什么 fido 没有 "$type": "MyApp.Dog, MyApp", 喜欢它的小狗吗? :)


更新:我从this question 接受的答案中发现,我可以通过这样做来强制添加 $type:

var json = JsonConvert.SerializeObject(fido,
    typeof(Animal),
    new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        Formatting = Formatting.Indented
    });

但我的问题仍然存在 - 为什么 Json.NET 不按照文档自行执行此操作?

【问题讨论】:

标签: serialization types json.net


【解决方案1】:

简答:不会,因为它不能

正如您在问题中所述,将 TypeNameHandling 设置为 Auto 会指示 Json.Net 在被序列化的对象的实际(运行时)类型与其声明的类型不同时包含 .NET 类型名称(编译时)类型。为了做到这一点,Json.Net 需要知道每个对象的两种类型。

对于根对象内的所有内容,这很简单:只需通过GetType() 获取根对象的运行时类型,然后使用反射获取其所有声明的属性及其类型,并为每一个比较声明的类型到实际类型,看看它们是否不同。如果是,则输出类型名称。

但对于根对象本身,Json.Net 无法访问这两种类型。它拥有的所有信息都是fido引用的对象,其运行时类型为Dog。 Json.Net 无法发现 fido 变量被声明为 Animal,除非您以某种方式提供该上下文。这正是 Json.Net 提供 SerializeObject 重载的原因,它允许您指定被序列化对象的编译时类型。如果您希望TypeNameHandling.Auto 设置适用于根对象,您必须使用这些重载之一。

【讨论】:

  • 您可以在较新的版本中:ser.Serialize(stream, object, typeof(BaseClass)); 将基类传递给 serialize 方法,如果对象和预期类型不匹配,TypeNameHandling 将写入 $type。
  • @PopCatalin 对。这就是我在上面答案的最后两句话中所说的。
  • 你如何让它在控制器中的 ASP.NET Core 的序列化机制中使用那些 SerializeObject 重载之一?
  • @rory.ap 听起来很适合new question
【解决方案2】:

Brian 是绝对正确的,Json.NET 无法知道它正在传递的对象的编译时声明类型,因为 value 参数被声明为 object。解决这个问题的简单方法是,如果 Json.NET 添加通用序列化方法,以便编译时声明的类型会自动流向 Json.NET,但库的作者已决定反对我对 here 的提议。

作为替代方案,我已经将我所有的 json(反)序列化需求包装在一个带有通用序列化方法的 JsonHelper 类中,这些方法使用 typeof 表达式自动传递编译时声明的值类型序列化。

【讨论】:

  • 有趣的是,我认为要指定的类型应该是对象的类型,但实际上它应该与对象的实际类型不同来写出类型。简单地说,如果你想让它总是给根类型,在方法中添加typeof(object)而不是typeof(myObject)
【解决方案3】:

较新版本的 Json.Net 允许您将预期类型传递给序列化方法

 ser.Serialize(stream, rootObject, typeof(BaseClass));

您可以将基类传递给 serialize 方法,如果对象和预期类型不匹配,TypeNameHandling.Auto 将写入 $type。

【讨论】:

  • 如此简单。谢谢你!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-08
  • 1970-01-01
  • 1970-01-01
  • 2019-05-05
  • 1970-01-01
  • 1970-01-01
  • 2015-02-28
相关资源
最近更新 更多