IEnumerable的Json.Net序列化与TypeNameHandling = auto

时间:2016-01-07 18:53:18

标签: c# json.net

根据Json.Net documentation,所有IEnumerable类型都应序列化为json数组。

所以我期待以下课程:

public class MyClass
{
    public IEnumerable<string> Values { get; set; }
}

序列化为:

{
    "Values": []
}

问题在于,当我使用TypeNameHandling=Auto时,我得到:

{
    "Values": {
        "$type": "System.String[], mscorlib",
        "$values": []
    }
}

我需要TypeNameHandling=Auto用于其他属性,但我希望IEnumerable使用默认序列化。其他类型(例如IList)按预期工作。

这是一个错误还是我错过了什么?

这里是重现问题的完整代码:

    [Test]
    public void Newtonsoft_serialize_list_and_enumerable()
    {
        var target = new Newtonsoft.Json.JsonSerializer
        {
            TypeNameHandling = TypeNameHandling.Auto
        };

        var myEvent = new MyClass
        {
            Values = new string[0]
        };

        var builder = new StringWriter();
        target.Serialize(builder, myEvent);
        var json = JObject.Parse(builder.ToString());

        Assert.AreEqual(JTokenType.Array, json["Values"].Type);
    }

    public class MyClass
    {
        public IEnumerable<string> Values { get; set; }
    }

我正在使用Newtonsoft 7.0.1。

更新: 这里使用更多类型的另一个测试:

    [Test]
    public void Newtonsoft_serialize_list_and_enumerable()
    {
        var target = new Newtonsoft.Json.JsonSerializer
        {
            TypeNameHandling = TypeNameHandling.Auto
        };

        var myEvent = new MyClass
        {
            Values1 = new string[0],
            Values2 = new List<string>(),
            Values3 = new string[0],
            Values4 = new List<string>(),
            Values5 = new string[0]
        };

        var builder = new StringWriter();
        target.Serialize(builder, myEvent);
        var json = builder.ToString();
    }

    public class MyClass
    {
        public IEnumerable<string> Values1 { get; set; }
        public IEnumerable<string> Values2 { get; set; }
        public IList<string> Values3 { get; set; }
        public IList<string> Values4 { get; set; }
        public string[] Values5 { get; set; }
    }

这就是结果:

{
    "Values1": {
        "$type": "System.String[], mscorlib",
        "$values": []
    },
    "Values2": [],
    "Values3": {
        "$type": "System.String[], mscorlib",
        "$values": []
    },
    "Values4": [],
    "Values5": []
}

我再也不明白为什么根据组合得到不同的结果。

2 个答案:

答案 0 :(得分:4)

在反序列化为IEnumerableIList字段时,Json.Net的默认自动行为是创建List实例。如果您分配Array实例,那么将对象恢复到原始实例状态的唯一方法是让Json.Net添加$type元数据,这就是您所看到的。 即。有很多方法可以反序列化为IEnumerable字段。

使用List<string>实例:

var myEvent = new MyClass
{
    Values = new List<string>(),
};

使用:

public class MyClass
{
   public IEnumerable<string> Values { get; set; }
}    

结果(序列化时):

{"Values":["hello"]}

此外,如果您使用显式可构造类型或使用默认List,那么Json.Net将使用它并省略$type;

例如:

string[] Values { get; set; } = new string[0]; // not needed
IEnumerable<string> Values { get; set; } = new string[0]; //$type needed
IEnumerable<string> Values { get; set; } = new Stack<string>(); //$type needed
IEnumerable<string> Values { get; set; } = new List<string>; // not needed
List<string> Values { get; set; } = new List<string>; // not needed

ObservableCollection<string> Values { get; set; } = 
    new ObservableCollection<string>(); // not needed

答案 1 :(得分:2)

这不是错误,但这是预期的行为。您正在全局设置TypeNameHandling。您可以使用下面的属性标记属性,而不是全局设置类型名称处理,以避免它们被包含在设置TypeNameHandling.Auto中:

[JsonProperty(TypeNameHandling = TypeNameHandling.None)]

以下是一个示例:

    public class MyClass
    {
        // Indicate the property not to apply any type handling
        [JsonProperty(TypeNameHandling=TypeNameHandling.None)]
        public IList<string> Values { get; set; }

        public string Name { get; set; }
    }

这将为您提供所需的结果,而不会受到全局声明的任何TypeNameHandling设置的影响。