23

我刚刚开始使用 Newtonsoft.Json (Json.net)。在我的第一个简单测试中,我在反序列化通用列表时遇到了问题。在下面的代码示例中,我序列化了一个对象,其中包含三种类型的简单整数列表(属性、成员 var 和数组)。

生成的 json 看起来不错(列表被转换为 json 数组)。但是,当我将 json 反序列化回相同类型的新对象时,所有列表项都会重复,期望数组。我已经通过第二次序列化它来说明这一点。

通过四处搜索,我了解到反序列化器也填充的列表可能有一个“私有”支持字段。

所以我的问题是:在以下情况下是否有(最好是简单的)避免重复项目的方法?

代码

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace JsonSerializeExample
{
    public class Program
    {
        static void Main()
        {
            var data = new SomeData();
            var json = JsonConvert.SerializeObject(data);
            Console.WriteLine("First : {0}", json);
            var data2 = JsonConvert.DeserializeObject<SomeData>(json);
            var json2 = JsonConvert.SerializeObject(data2);
            Console.WriteLine("Second: {0}", json2);
        }
    }

    public class SomeData
    {
        public string SimpleField;
        public int[] IntArray;
        public IList<int> IntListProperty { get; set; }
        public IList<int> IntListMember;

        public SomeData()
        {
            SimpleField = "Some data";
            IntArray = new[] { 7, 8, 9 };
            IntListProperty = new List<int> { 1, 2, 3 };
            IntListMember = new List<int> { 4, 5, 6 };
        }
    }
}

结果输出

First : {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6],"IntListProperty":[1,2,3]}
Second: {"SimpleField":"Some data","IntArray":[7,8,9],"IntListMember":[4,5,6,4,5,6],"IntListProperty":[1,2,3,1,2,3]}

这里可能与Json.Net 重复私有列表项有一些重叠。但是,我认为我的问题更简单,我仍然没有弄清楚。

4

4 回答 4

27

那是因为您在构造函数中添加项目。处理列表时反序列化器的一种常用方法基本上是:

  • 通过 getter 读取列表
    • 如果列表为空:创建一个新列表并通过属性设置器分配,如果有
  • 依次反序列化每个项目,并将 ( Add) 附加到列表中

这是因为大多数列表成员没有设置器,即

public List<Foo> Items {get {...}} // <=== no set

与数组对比,数组必须有一个 setter 才能有用;因此该方法通常是:

  • 依次反序列化每个项目,并将 ( Add) 附加到临时列表
  • 将列表转换为数组 ( ToArray),并通过 setter 分配

一些序列化程序为您提供控制此行为的选项(其他序列化程序没有);并且一些序列化程序使您能够完全绕过构造函数(其他序列化程序则没有)。

于 2012-11-15T09:18:32.980 回答
12

我很确定这篇文章不再相关,但为了将来参考,这里有一个可行的解决方案。只需指定ObjectCreationHandling设置为Replace,即始终创建新对象而不是Auto(这是默认设置)即重用现有对象,在需要时创建新对象。

var data = new SomeData(); 
var json = JsonConvert.SerializeObject(data);
Console.WriteLine("First : {0}", json);
var data2 = JsonConvert.DeserializeObject<SomeData>(json, new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace });
var json2 = JsonConvert.SerializeObject(data2);
Console.WriteLine("Second: {0}", json2);
于 2019-11-13T21:47:26.137 回答
10

我遇到了类似的问题,但根本原因不同。我正在序列化和反序列化一个看起来像这样的类:

public class Appointment
{
    public List<AppointmentRevision> Revisions { get; set; }

    public AppointmentRevision CurrentRevision
    {
        get { return Revision.LastOrDefault(); }
    }

    public Appointment()
    {
        Revisions = new List<AppointmentRevision>();
    }
}

public class AppointmentRevision
{
    public List<Attendee> Attendees { get; set; }
}

当我序列化这个时, CurrentRevision 也被序列化了。我不确定如何,但是当它反序列化时,它正确地保留了 AppointmentRevision 的单个实例,但在与会者列表中创建了重复项。解决方案是在 CurrentRevision 属性上使用 JsonIgnore 属性。

public class Appointment
{
    public List<AppointmentRevision> Revisions { get; set; }

    [JsonIgnore]   
    public AppointmentRevision CurrentRevision
    {
        get { return Revision.LastOrDefault(); }
    }

    public Appointment()
    {
        Revisions = new List<AppointmentRevision>();
    }
}
于 2014-10-08T17:35:58.027 回答
1

反序列化 JSON 时如何将 ObjectCreationHandling.Replace 应用于选定的属性?

事实证明(我在 2019 年),您可以像在问题中所做的那样在构造函数中设置列表项。我在列表声明上方添加了 ObjectCreationHandling.Replace 属性,然后序列化应该用 JSON 替换列表中存储的任何内容。

于 2019-04-24T17:14:24.750 回答