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

时间:2016-08-09 19:36:20

标签: serialization types json.net

当我将Json.NET设置为使用TypeNameHandling设置为TypeNameHandling.Auto进行序列化时,它会正确设置对象的子属性的$ type,但不会对要序列化的根对象设置$ 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不按照文档自行完成?

3 个答案:

答案 0 :(得分:6)

简短回答:不是因为不能

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

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

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

答案 1 :(得分:1)

Brian绝对正确,当AVCaptureConnection参数声明为times = c(0,30,60,90,120,180,240) # Transpose cin dataframe cin <- as.data.frame(t(cin[,2:8])) # Apply the calculation to each column and bind results to bottom of dataframe cin <- rbind(cin, apply(cin, 2, function(x) sum(diff(times[order(times)])*rollmean(x,2)))) # Set rowname for calculation to "AUC" row.names(cin)[length(row.names(cin))] <- "AUC" # Transpose cin dataframe back to original format cin <- as.data.frame(t(cin)) 时,Json.NET无法知道传递的对象的编译时声明类型。对此的简单修复是,如果Json.NET添加了通用序列化方法,那么编译时声明的类型将自动转移到Json.NET,但库的作者已决定反对我对此here的提议。

作为替代方案,我在value类中包含了所有json(de)序列化需求,并使用通用序列化方法,这些方法使用object表达式自动传递编译时声明的类型要序列化的值。

答案 2 :(得分:0)

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

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

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