自引用属性的反序列化不起作用

时间:2011-06-08 14:59:05

标签: json.net

我的这个对象有一个Parent属性,它引用了同一类型的另一个对象:

[JsonObject(IsReference = true)]
class Group
{
    public string Name { get; set; }

    public Group(string name)
    {
        Name = name;
        Children = new List<Group>();
    }

    public IList<Group> Children { get; set; }

    public Group Parent { get; set; }

    public void AddChild(Group child)
    {
        child.Parent = this;
        Children.Add(child);
    }
}

序列化工作正常,导致json看起来像这样:

{
  "$id": "1",
  "Name": "Parent",
  "Children": [
    {
      "$id": "2",
      "Name": "Child",
      "Children": [],
      "Parent": {
        "$ref": "1"
      }
    }
  ],
  "Parent": null
}

但是反序列化不起作用。 Parent属性返回null。

测试如下:

[Test]
public void Test()
{
    var child = new Group("Child");
    var parent = new Group("Parent");
    parent.AddChild(child);

    var json = JsonConvert.SerializeObject(parent, Formatting.Indented);
    Debug.WriteLine(json);

    var deserializedParent = (Group) JsonConvert.DeserializeObject(json, typeof(Group));
    Assert.IsNotNull(deserializedParent.Children.First().Parent);
}

我做错了什么?任何帮助表示赞赏!

3 个答案:

答案 0 :(得分:4)

使用引用不适用于只包含带参数的构造函数的对象。

Json.NET必须在创建父项之前反序列化所有子值,它需要将这些值传递给构造函数,因此没有有效的父引用分配给子项。

答案 1 :(得分:2)

为了扩展James的答案,您可以通过为Json.Net提供无参数(默认)构造函数来解决此问题。如果您愿意,它可以是私有的,只要您还使用[JsonConstructor]属性标记它。

[JsonObject(IsReference = true)]
class Group
{
    ...

    [JsonConstructor]
    private Group()
    {
    }

    public Group(string name)
    {
        Name = name;
        Children = new List<Group>();
    }

    ...
}

这种安排允许Json.Net创建对象而无需预先获得所有信息;然后它可以使用公共属性来填充事物。

小提琴:https://dotnetfiddle.net/QfqV43

答案 2 :(得分:0)

我发现,另一种方法是非常固执地创建两阶段反序列化,
正如我在以下Deserializing Circular References by Two-Phase deserialization

中所述

通常,我创建两个IContractResolver,一个用于仅反序列化构造方法的属性(如果它具有参数)。

在第二阶段,我使用常规ContractResolver填充对象。