2016-03-03 79 views
3

相当新的Json.net并试图序列化,然后deserialing对象得到下面的错误下面这个简单的例子:反序列化一个IEnumerable <T>使用[DataContract]适用不起作用

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> 
    /// Summary description for JsonError 
    /// </summary> 
    [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<Timehunter.Base.Act.Activities>(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<Activity> 
    { 
     private List<Activity> _list; 
     [DataMember] 
     public List<Activity> List 
     { 
      get { return this._list; } 
      set { this._list = value; } 
     } 
     public Activities() 
     { 
      this._list = new List<Activity>(); 
     } 

     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<Activity> 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. 

我在做什么错?

回答

4

更新

报告为Issue #1598: DataContractAttribute does not cause JSon object serialization for IEnumerable并固定在提交e9e2d00。它应该是10.0.3之后的下一个版本这将可能是Json.NET版本11

原来的答复

我注意到你有标记您的Activities[DataContract][DataMember]

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

应用[DataContact]将导致DataContractJsonSerializerIEnumerable<T>作为具有属性的JSON对象进行序列化,而不是作为JSON数组。由于Json.NET supports data contract attributes应用于非枚举类型时,您可能会认为它也会尊重枚举类和集合类。

但是,看来这没有实现。如果我系列化你的类DataContractJsonSerializer,我看到

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

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

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

再后来它反序列化,因为在抛出一个异常它不知道如何将成员添加到您的IEnumerable<Activity>类。 (如果您的班级实施了ICollection<Activity>had a constructor with an IEnumerable<Activity> argument,本来可以添加成员。)

那么,应该这样工作吗?文档页面Serialization Attributes指出:

The DataContractAttribute can be used as substitute for JsonObjectAttribute. The DataContractAttribute will default member serialization to opt-in.

这意味着Json.NET应该工作,你所期望的方式。如果你愿意的话,你可以用report an issue - 至少应该说明文档。

作为一种变通方法,如果要强制Json.NET序列化的集合作为一个对象,你需要使用[JsonObject]代替:

[DataContract] 
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] 
public class Activities : IEnumerable<Activity> 
{ 
    private List<Activity> _list; 

    [DataMember] 
    [JsonProperty] 
    public List<Activity> List 
    { 
     get { return this._list; } 
     set { this._list = value; } 
    } 

    // Remainder unchanged. 
} 

如果你有很多枚举类施加[DataContract],或者不能添加对Json的依赖。NET到你的模型,你可以创建一个custom 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 
}; 
+0

感谢名单,但我不得不更改CreateContract方法有点为了让它为我工作:'代码'... &&!t.GetCustomAttributes(typeof(JsonContainerAttribute),true).Any()) if(t.GetCustomAttributes( typeof(DataContractAttribute),true).Any()) 返回base.CreateObjectContract(objectType); ... – hawi

+0

@hawi - 按照您的建议更新答案。看起来像Json.NET [将DataContract视为继承,即使数据协定序列化程序没有。](http://json.codeplex.com/discussions/357850)。 – dbc