当TypeNameHandling为Auto时,为什么Json.NET不包含根对象的$ type?

uri*_*rig 7 serialization types json.net

当我将Json.NET设置为序列化并将TypeNameHandling设置为TypeNameHandling.Auto时,它正确地为对象的子属性设置$ type,但是对于被序列化的根对象没有这样做.为什么?

请考虑以下repro:

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
    });
Run Code Online (Sandbox Code Playgroud)

发送到json变量的Json 是:

{
    "Offspring": [{
        "$type": "MyApp.Dog, MyApp",
        "Offspring": null
    }]
}
Run Code Online (Sandbox Code Playgroud)

Json.NET 文档说,TypeNameHandling.Auto行为是:

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

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


更新:我从这个问题的接受答案中发现,我可以通过执行以下操作强制添加$ type:

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

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

Bri*_*ers 6

简短的回答:它不是因为它不能.

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

对于根对象内部的所有内容,这很简单:只需获取根对象的运行时类型GetType(),然后使用反射获取其所有声明的属性及其类型,并为每个属性将声明的类型与实际类型进行比较如果他们不一样 如果是,则输出类型名称.

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

  • @PopCatalin 对。这就是我在上面回答的最后两句话中所说的。 (2认同)