【问题标题】:Json.net serialization of custom collection implementing IEnumerable<T>实现 IEnumerable<T> 的自定义集合的 Json.net 序列化
【发布时间】:2011-11-25 21:30:03
【问题描述】:

我有一个实现 IEnumerable 的集合类,但在反序列化相同的序列化版本时遇到问题。我正在使用 Json.net v 4.0.2.13623

这是我的集合类的简化版本,它说明了我的问题

public class MyType
{
  public int Number { get; private set; }
  public MyType(int number)
  {
    this.Number = number;
  }
}

public class MyTypes : IEnumerable<MyType>
{
  private readonly Dictionary<int, MyType> c_index;
  public MyTypes(IEnumerable<MyType> seed)
  {
    this.c_index = seed.ToDictionary(item => item.Number);
  }
  public IEnumerator<MyType> GetEnumerator()
  {
    return this.c_index.Values.GetEnumerator();
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
    return this.GetEnumerator();
  }
  public int CustomMethod()
  {
    return 1;  // Just for illustration
  }
}

当我序列化它时,我得到以下 json

[
  {
    "Number": 1
  },
  {
    "Number": 2
  }
]

但是当我尝试反序列化时,我得到以下异常 System.Exception:无法创建和填充列表类型 MyTypes

也尝试过使用序列化提示,但没有成功

这些是我用过的测试功能

public void RoundTrip()
{
  var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });
  var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented);
  var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent);
}
public void RoundTripWithSettings()
{
  var _myTypes1 = new MyTypes(new[] { new MyType(1), new MyType(2) });

  var _serializationSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Arrays };
  var _jsonContent = JsonConvert.SerializeObject(_myTypes1, Formatting.Indented, _serializationSettings);
  var _myTypes2 = JsonConvert.DeserializeObject<MyTypes>(_jsonContent, _serializationSettings);
}

有没有人设法序列化他们自己的集合对象?

提前致谢

拍拍

【问题讨论】:

  • 您应该序列化和反序列化到具体的集合,例如 List 而不是 IEnumerable。要反序列化某些东西,它需要一个实现,而不仅仅是一个接口。
  • 克里斯,通过其他一些更改更改为 ReadOnlyCollection 的子类,但我可以知道反序列化集合
  • 其实你也可以使用 IList 。它将为您创建一个 List。但是没有为 IEnumerable 定义默认集合。

标签: c# json serialization json.net


【解决方案1】:

从 Json.NET 6.0 第 3 版及更高版本(我在 8.0.3 上测试)开始,只要实现 IEnumerable&lt;MyType&gt; 的自定义类有一个接受 IEnumerable&lt;MyType&gt; 输入参数的构造函数,它就会自动工作。来自release notes

致所有未来不可变 .NET 集合的创建者:如果您的 T 集合有一个采用 IEnumerable 的构造函数,那么 Json.NET 将在反序列化到您的集合时自动工作,否则您将完全不走运。

由于问题中的MyTypes 类只有这样一个构造函数,所以现在可以正常工作。演示小提琴:https://dotnetfiddle.net/LRJHbd.

如果没有这样的构造函数,则需要添加一个无参数构造函数并使用有效的Add(MyType item) 方法实现ICollection&lt;MyType&gt;(而不仅仅是IEnumerable&lt;MyType&gt;)。然后 Json.NET 可以构造和填充集合。或反序列化为original answer 中的中间集合。

【讨论】:

    【解决方案2】:

    我相信 JSON .NET 依赖于它反序列化到的类型的无参数构造函数。从反序列化的角度来看,它不知道向构造函数提供什么。

    至于您的集合,它又应该如何知道如何填充 IEnumerable&lt;T&gt; 的实现,它只定义了如何枚举集合而不是如何填充它。您应该直接反序列化为一些标准的IEnumerable&lt;MyType&gt;(或直接反序列化到我相信 JSON.NET 支持的IEnumerable&lt;MyType&gt;),然后将其传递给您的构造函数。

    我相信以下所有方法都应该适用于这种情况:

    var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<MyTypes[]>(_jsonContent));
    var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<List<MyTypes>>(_jsonContent));
    var _myTypes2 = new MyTypes(JsonConvert.DeserializeObject<IEnumerable<MyTypes>>(_jsonContent));
    

    或者,虽然需要做更多的工作,但如果您需要直接反序列化,您可以在您的集合上实现类似 IList 的东西,这应该允许 JSON.NET 使用 IList 方法来填充您的集合。

    【讨论】:

    • 自从这个答案以来,库已经扩展了它的功能——它将与无参数构造函数一起工作(就像它一直有的那样),但它也支持创建一个实例,该实例通过构造函数获取其所有数据,并且它将支持具有采用 IEnumerable 的构造函数的集合类型(正如@dbc 提到的答案)。
    猜你喜欢
    • 1970-01-01
    • 2011-02-22
    • 2016-10-28
    • 1970-01-01
    • 2014-11-08
    • 2017-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多