使用反序列化IEnumerable 不起作用

相当新的Json.net并尝试以下简单示例序列化然后解除对象获取以下错误:

using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Collections; namespace Timehunter.Base.ServicesTests { ///  /// Summary description for JsonError ///  [TestClass] public class JsonError { [TestMethod] public void TestMethod1() { JsonSerializerSettings serializerSettings = new JsonSerializerSettings() { DateFormatHandling = DateFormatHandling.IsoDateFormat, DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset }; Act.Activities acts = new Act.Activities(); acts.Add(new Act.Activity() { Id = 1, Name = "test1" }); acts.Add(new Act.Activity() { Id = 2, Name = "test2" }); string json = Newtonsoft.Json.JsonConvert.SerializeObject(acts, serializerSettings); Timehunter.Base.Act.Activities target = Newtonsoft.Json.JsonConvert.DeserializeObject(json, serializerSettings); Assert.AreEqual("test1", target.List[0].Name, "Name of first activity"); } } } namespace Timehunter.Base { [DataContract] public class Activity { private int _id; private string _name; [DataMember] public int Id { get { return this._id; } set { this._id = value; } } [DataMember] public string Name { get { return this._name; } set { this._name = value; } } public Activity() { this._id = new int(); this._name = string.Empty; } } [DataContract] public class Activities : IEnumerable { private List _list; [DataMember] public List List { get { return this._list; } set { this._list = value; } } public Activities() { this._list = new List(); } public void Add(Activity item) { this._list.Add(item); } public bool Remove(Activity item) { return this._list.Remove(item); } public int Count() { return this._list.Count; } public IEnumerator GetEnumerator() { return this._list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } 

然后我收到以下错误:

 Test Name: TestMethod1 Test FullName: Timehunter.Base.ServicesTests.JsonError.TestMethod1 Test Source: C:\Users\hawi.HAWCONS\Documents\Visual Studio 2015\Projects\Timehunter.Data\Timehunter.Base.ServicesTests\JsonError.cs : line 67 Test Outcome: Failed Test Duration: 0:00:00,2038359 Result StackTrace: at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewList(JsonReader reader, JsonArrayContract contract, Boolean& createdFromNonDefaultCreator) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at Timehunter.Base.ServicesTests.JsonError.TestMethod1() in C:\Users\hawi.HAWCONS\Documents\Visual Studio 2015\Projects\Timehunter.Data\Timehunter.Base.ServicesTests\JsonError.cs:line 79 Result Message: Test method Timehunter.Base.ServicesTests.JsonError.TestMethod1 threw exception: Newtonsoft.Json.JsonSerializationException: Cannot create and populate list type Timehunter.Base.Act.Activities. Path '', line 1, position 1. 

我究竟做错了什么?

更新2

为了向后兼容性,这将在11.0.2中恢复。 有关解决方案,请参阅原始答案。

更新

报告为问题#1598:DataContractAttribute不会导致IEnumerable的JSon对象序列化并在commit e9e2d00 。 它应该在10.0.3之后的下一个版本中,它可能是Json.NET版本11。

原始答案

我注意到你用[DataContract][DataMember]标记了你的Activities类:

 [DataContract] public class Activities : IEnumerable { private List _list; [DataMember] public List List { get { return this._list; } set { this._list = value; } } // ... } 

应用[DataContact]将导致DataContractJsonSerializerIEnumerable序列化为具有属性的JSON对象,而不是作为JSON数组。 由于Json.NET在应用于非枚举时支持数据协定属性 ,因此您可能会认为它也会在枚举和集合上尊重它们。

但是,它似乎没有实现。 如果我用DataContractJsonSerializer序列化你的类,我明白了

 {"List":[{"Id":1,"Name":"test1"},{"Id":2,"Name":"test2"}]} 

但是如果我使用Json.NET序列化,我会看到[DataContract]被忽略了:

 [{"Id":1,"Name":"test1"},{"Id":2,"Name":"test2"}] 

然后它会在反序列化期间抛出exception,因为它不知道如何向IEnumerable类添加成员。 (如果你的类实现了ICollection ,或者有一个带有IEnumerable参数的构造函数,它本来可以添加成员。)

那么,这应该有用吗? 文档页面序列化属性指出:

DataContractAttribute可以用作JsonObjectAttribute的替代。 DataContractAttribute将默认成员序列化为opt-in。

这意味着Json.NET应该以您期望的方式工作。 如果您愿意,可以报告相关问题 – 至少应该澄清文档。

作为一种解决方法 ,如果要强制Json.NET将集合序列化为对象,则需要使用[JsonObject]

 [DataContract] [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Activities : IEnumerable { private List _list; [DataMember] [JsonProperty] public List List { get { return this._list; } set { this._list = value; } } // Remainder unchanged. } 

如果你有许多应用了[DataContract]可枚举类,或者无法在模型中添加对Json.NET的依赖,你可以创建一个自定义的ContractResolver来检查可枚举类上是否存在[DataContract]并将它们序列化为对象:

 public class DataContractForCollectionsResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." static DataContractForCollectionsResolver instance; static DataContractForCollectionsResolver() { instance = new DataContractForCollectionsResolver(); } public static DataContractForCollectionsResolver Instance { get { return instance; } } protected DataContractForCollectionsResolver() : base() { } protected override JsonContract CreateContract(Type objectType) { var t = (Nullable.GetUnderlyingType(objectType) ?? objectType); if (!t.IsPrimitive && t != typeof(string) && !t.IsArray && typeof(IEnumerable).IsAssignableFrom(t) && !t.GetCustomAttributes(typeof(JsonContainerAttribute),true).Any()) { if (t.GetCustomAttributes(typeof(DataContractAttribute),true).Any()) return base.CreateObjectContract(objectType); } return base.CreateContract(objectType); } } 

然后使用以下设置:

 var serializerSettings = new JsonSerializerSettings() { DateFormatHandling = DateFormatHandling.IsoDateFormat, DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset, ContractResolver = DataContractForCollectionsResolver.Instance };