反序列化关系模型的Newtonsoft.Json问题 [英] Newtonsoft.Json issue with deserialising relational model

查看:78
本文介绍了反序列化关系模型的Newtonsoft.Json问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有父子关系的简单关系模型,如下所示:

I've a simple relational model with a Parent and Child relation as follows:

    public class Parent
    {
        public Parent(int id)
        {
            Id = id;
        }

        public int Id { get; private set; }
        public IList<Child> Children { get; set; } = new List<Child>();
    }

    public class Child
    {
        public Parent Parent { get; set; }
    }

我创建了一个小对象图,该图由两个共享相同父代的孩子的列表组成:

I create a small object graph consisting of a list of two children who share the same parent:

    var parent = new Parent(1);
    var child1 = new Child {Parent = parent};
    var child2 = new Child {Parent = parent};
    parent.Children.Add(child1);
    parent.Children.Add(child2);

    var data = new List<Child> {child1, child2};

接下来,我使用SerializeObject将其序列化:

Next, I serialize this using SerializeObject:

    var settings = new JsonSerializerSettings
    {
        PreserveReferencesHandling = PreserveReferencesHandling.All
    };
    var json = Newtonsoft.Json.JsonConvert.SerializeObject(data, Formatting.Indented, settings);

据我所知,生成的json看起来不错:

As far I can see, the resulting json looks fine:

{
  "$id": "1",
  "$values": [
    {
      "$id": "2",
      "Parent": {
        "$id": "3",
        "Id": 1,
        "Children": {
          "$id": "4",
          "$values": [
            {
              "$ref": "2"
            },
            {
              "$id": "5",
              "Parent": {
                "$ref": "3"
              }
            }
          ]
        }
      }
    },
    {
      "$ref": "5"
    }
  ]
}

但是,当我反序列化json时,我没有得到预期的对象,因为对于第二个孩子,Parent属性为null,导致第二个断言失败:

However, when I deserialize the json I don't get the expected object because for the second child the Parent property is null, causing the second assertion to fail:

    var data2 = Newtonsoft.Json.JsonConvert.DeserializeObject<IList<Child>>(json, settings);
    Debug.Assert(data2[0].Parent != null);
    Debug.Assert(data2[1].Parent != null);

没有Parent的构造函数,则不会发生此问题,并且第二个孩子的Parent属性具有预期值.

Without the constructor of Parent this problem does not occur and the Parent property of the second child has the expected value.

有什么想法吗?

推荐答案

参数化的构造函数不能很好地与PreserveReferencesHandling一起使用,因为存在固有的鸡与蛋"问题:传递给构造函数的必要信息可能无法解决在反序列化器需要创建对象时从JSON加载.因此,需要一种方法来创建一个空对象,然后在以后填写适当的信息.

Parameterized constructors don't work well with PreserveReferencesHandling because there is an inherent chicken-and-egg problem: the necessary information to pass to the constructor may not be loaded from the JSON by the time the deserializer needs to create the object. So there needs to be a way for it to create an empty object and then fill in the appropriate information later.

这是已知的限制,记录:

This is a known limitation and is documented:

注意:
通过非默认构造函数设置值时,无法保留引用.对于非默认构造函数,必须在父值之前创建子值,以便可以将它们传递到构造函数中,从而无法进行跟踪引用. 类型是一个类的示例,其值使用非默认构造函数填充,而不适用于PreserveReferencesHandling.

Note:
References cannot be preserved when a value is set via a non-default constructor. With a non-default constructor, child values must be created before the parent value so they can be passed into the constructor, making tracking reference impossible. ISerializable types are an example of a class whose values are populated with a non-default constructor and won't work with PreserveReferencesHandling.

要解决模型问题,可以在Parent类中创建一个私有的,无参数的构造函数,并用[JsonConstructor]进行标记.然后用[JsonProperty]标记Id属性,这将允许Json.Net使用私有设置程序.因此,您将拥有:

To work around the problem with your model you could create a private, parameterless constructor in your Parent class and mark it with [JsonConstructor]. Then mark the Id property with [JsonProperty], which will allow Json.Net to use the private setter. So you would have:

public class Parent
{
    public Parent(int id)
    {
        Id = id;
    }

    [JsonConstructor]
    private Parent()
    { }

    [JsonProperty]
    public int Id { get; private set; }
    public IList<Child> Children { get; set; } = new List<Child>();
}

顺便说一句,由于对象之间没有共享任何列表,因此可以使用PreserveReferencesHandling.Objects而不是PreserveReferencesHandling.All.这样会使JSON变小,但仍会保留您关心的引用.

As an aside, since none of your Lists are shared amongst the objects, you could use PreserveReferencesHandling.Objects instead of PreserveReferencesHandling.All. This will make the JSON a little smaller, but will still preserve the references you care about.

在此处工作的演示: https://dotnetfiddle.net/cLk9DM

这篇关于反序列化关系模型的Newtonsoft.Json问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆