反序列化其他列表中的列表时,Newtonsoft反序列化会复制元素

时间:2016-02-16 22:58:43

标签: c# json.net deserialization

我有一个对象列表(A),每个对象包含一个对象列表(B)。我做了As列表的序列化没有问题但是 当我对每个A中的Bs列表进行反序列化时,其原始数量是Bs的两倍。为什么会这样?

        var sample = new List<A>
        {
            new A
            {
                Flag = true,
                Amount = 10,
                Bs = new List<B>
                {
                    new B {Amount = 4, Id = Guid.NewGuid()},
                    new B {Amount = 6, Id = Guid.NewGuid()}
                }
            },
            new A
            {
                Flag = true,
                Amount = 20,
                Bs = new List<B>
                {
                    new B {Amount = 4, Id = Guid.NewGuid()},
                    new B {Amount = 6, Id = Guid.NewGuid()}
                }
            },
            new A
            {
                Flag = false,
                Amount = 30,
                Bs = new List<B>
                {
                    new B {Amount = 4, Id = Guid.NewGuid()},
                    new B {Amount = 6, Id = Guid.NewGuid()}
                }
            }
        };

        var serialized = JsonConvert.SerializeObject(sample, ContractResolver.AllMembersSettings);
        var deserialized = JsonConvert.DeserializeObject<List<A>>(serialized, ContractResolver.AllMembersSettings);
class A
{
    public bool Flag { get; set; }
    public decimal Amount { get; set; }
    public List<B> Bs { get; set; }
}

class B
{
    public Guid Id { get; set; }
    public decimal Amount { get; set; }
}

public class ContractResolver : DefaultContractResolver
{
    public static readonly JsonSerializerSettings AllMembersSettings =
        new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            ContractResolver = new ContractResolver()
        };

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props =
            type
                .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(p => p.CanRead && p.CanWrite)
                .Select(p => base.CreateProperty(p, memberSerialization))
            .Union(
            type
                .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(f => base.CreateProperty(f, memberSerialization)))
            .ToList();

        props.ForEach(p => { p.Writable = true; p.Readable = true; });

        return props;
    }
}

3 个答案:

答案 0 :(得分:2)

之所以发生这种情况,是因为C#编译器会为属性生成一个支持字段。

你可以删除自定义创建的解析器,让Json.NET发挥其魔力,或者在最后使用小黑客。

Auto-Implemented Properties

  

自动实现(自动实现)属性可自动执行此操作   图案。更具体地说,非抽象属性声明是   允许使用分号访问器体。两个访问者都必须   现在,两者都必须有分号,但他们可以有   不同的可访问性修饰符。当指定属性时   这样,将自动生成一个支持字段   属性和访问器将实现读写   到那个支持领域。支持字段的名称是编译器   生成并且用户无法访问。

你可以通过一个小小的黑客来实现你想要的东西,尽管我会建议你 另外,如果您确实需要BindingFlags.NonPublic,我会重新考虑,因为单独删除它会解决您的问题。

小黑客

type
    .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
    .Where(field => !field.Name.EndsWith("k__BackingField"))
    .Select(f => base.CreateProperty(f, memberSerialization))
)

答案 1 :(得分:1)

正如我在问题中所提到的,我不确定你为何拥有ContractResolver,但是当我使用以下内容时:

string sampleData = Newtonsoft.Json.JsonConvert.SerializeObject(sample);
List<A> test = Newtonsoft.Json.JsonConvert.DeserializeObject<List<A>>(sampleData);

数据按预期序列化和反序列化。

答案 2 :(得分:0)

我通过将ObjectCreationHandling设置为Replace

解决了这个问题

来自Newtonsoft文档:

https://www.newtonsoft.com/json/help/html/DeserializeObjectCreationHandling.htm

型号:

public class UserViewModel
{
    public string Name { get; set; }
    public IList<string> Offices { get; private set; }

    public UserViewModel()
    {
        Offices = new List<string>
        {
            "Auckland",
            "Wellington",
            "Christchurch"
        };
    }
}

示例代码:

string json = @"{
  'Name': 'James',
  'Offices': [
    'Auckland',
    'Wellington',
    'Christchurch'
  ]
}";

UserViewModel model1 = JsonConvert.DeserializeObject<UserViewModel>(json);

foreach (string office in model1.Offices)
{
    Console.WriteLine(office);
}
// Auckland
// Wellington
// Christchurch
// Auckland
// Wellington
// Christchurch

UserViewModel model2 = JsonConvert.DeserializeObject<UserViewModel>(json, new JsonSerializerSettings
{
    ObjectCreationHandling = ObjectCreationHandling.Replace
});

foreach (string office in model2.Offices)
{
    Console.WriteLine(office);
}

// Auckland
// Wellington
// Christchurch

我真的不明白为什么要使用默认设置来复制它们,或者为什么要使用它们,但是它们的例子正是我正在发生的事情。