Runtime: Deserialization error - Reference '5' not found. Path: $.Test.$values[0].$ref.

Created on 30 Nov 2020  路  4Comments  路  Source: dotnet/runtime

Description

System.Text.Json exception thrown deserializing json. Json was serialized using the same framework version.

Reference '5' not found. Path: $.Test.$values[0].$ref | LineNumber: 34 | BytePositionInLine: 19.

Note: the two objects named 'Test' are separate instances.

{
  "$id": "1",
  "Root": {
    "$id": "2",
    "$values": [
      {
        "$id": "3",
        "Id": "66968da1-17c4-4180-8a74-b3e3ee066e59",
        "Name": "Root",
        "Parent": null,
        "Children": {
          "$id": "4",
          "$values": [
            {
              "$id": "5",
              "Id": "6edc6837-1a73-4a67-9377-4b62684dfb29",
              "Name": "Test",
              "Parent": {
                "$ref": "3"
              },
              "Children": {
                "$id": "6",
                "$values": []
              }
            }
          ]
        }
      }
    ]
  },
  "Test": {
    "$id": "7",
    "$values": [
      {
        "$ref": "5"
      },
      {
        "$id": "8",
        "Id": "b5ec04f5-f348-46dc-a5f0-44c95b181a72",
        "Name": "Test",
        "Parent": {
          "$id": "9",
          "Id": "222062ca-c4c5-4a5a-af9f-abeb89bcd8a5",
          "Name": "Kent",
          "Parent": null,
          "Children": {
            "$id": "10",
            "$values": [
              {
                "$ref": "8"
              }
            ]
          }
        },
        "Children": {
          "$id": "11",
          "$values": []
        }
      }
    ]
  },
  "Kent": {
    "$id": "12",
    "$values": [
      {
        "$ref": "9"
      }
    ]
  }
}

Configuration

net5.0 (dotnet-sdk-5.0.100-win-x64)
Windows 10 Pro 64bit
10.0.19042 Build 19042

Other information

public sealed class Link
{
    [JsonInclude] 
    public Guid Id { get; }

    [JsonInclude] 
    public string Name { get; }

    [JsonInclude] 
    public Link Parent { get; }

    [JsonInclude] 
    public List<Link> Children { get; }

    public Link()
    { 
    }

    public Link(Link parent, string name)
    {
        Parent = parent;
        Id = Guid.NewGuid();
        Name = name ?? Id.ToString();
        Children = new List<Link>();
    }
}

Serialized object:

public Dictionary<string, List<Link>> Links { get; private set; } = new Dictionary<string, List<Link>>();

Deserializing code:

var jsonSerializerOptions = new JsonSerializerOptions
{
    MaxDepth = 10000,
    ReferenceHandler = ReferenceHandler.Preserve
};

if (!string.IsNullOrWhiteSpace(filename))
{
    using Stream reader = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
    Links = await JsonSerializer.DeserializeAsync<Dictionary<string, List<Link>>>(reader, jsonSerializerOptions);
}
area-System.Text.Json

Most helpful comment

Parent and Children need to have a setter in order to be properly deserialized; otherwise, the payload for those objects will be ignored and hence, the reference (in this case "5") not being saved.

@justcurious can you try adding a setter for those properties?

public sealed class Link
{
    [JsonInclude] 
    public Link Parent { get; private set; }

    [JsonInclude] 
    public List<Link> Children { get; private set; }
}

All 4 comments

Parent and Children need to have a setter in order to be properly deserialized; otherwise, the payload for those objects will be ignored and hence, the reference (in this case "5") not being saved.

@justcurious can you try adding a setter for those properties?

public sealed class Link
{
    [JsonInclude] 
    public Link Parent { get; private set; }

    [JsonInclude] 
    public List<Link> Children { get; private set; }
}

@Jozkee is there easy enough detection in the serializer to throw an exception when metadata comes in for a property that can't be set? Would it be beneficial? Just thinking of we can make the root cause of issues like this clearer to callers.

Thanks @Jozkee , that makes sense

@layomia I think that would imply replacing calls to Utf8JsonReader (Try)Skip method with a custom method that "reads and validates" when certain features, such as ReferenceHandler, are turned on.

Would it be beneficial?

Right now the only case that I can think that would benefit from it would be ReferenceHandler.Preserve and the only benefit I can see is better exception messages.

Was this page helpful?
0 / 5 - 0 ratings