Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialization of self-referencing properties does not work

Tags:

json.net

I have this object with a Parent property that reference another object of the same type:

[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);
    }
}

Serialization works fine and results in json looking like this:

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

But deserialization doesn't work. The Parent property comes back null.

A test looks like this:

[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);
}

What am I doing wrong? Any help appreciated!

like image 414
LinusK Avatar asked Jun 08 '11 14:06

LinusK


2 Answers

Using references doesn't work with objects that only have constructors with parameters.

Json.NET has to deserialize all the child values before it creates the parent, it needs those values to pass to the constructor, so there is no valid parent reference to assign to the child.

like image 97
James Newton-King Avatar answered Sep 19 '22 15:09

James Newton-King


To expand on James's answer, you can fix this issue by providing a parameterless (default) constructor for Json.Net to use. It can be private if you want, so long as you also mark it with a [JsonConstructor] attribute.

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

    [JsonConstructor]
    private Group()
    {
    }

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

    ...
}

This arrangement allows Json.Net to create the object without needing all the information up front; it can then use the public properties to fill things in afterward.

Fiddle: https://dotnetfiddle.net/QfqV43

like image 27
Brian Rogers Avatar answered Sep 19 '22 15:09

Brian Rogers