通过两阶段反序列化反序列化循环引用 [英] Deserializing Circular References by Two-Phase deserialization

查看:121
本文介绍了通过两阶段反序列化反序列化循环引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用引用PreserveReferencesHandling = PreserveReferencesHandling.All的序列化器/反序列化器.

I have a Serializer/Deserializer that use a references PreserveReferencesHandling = PreserveReferencesHandling.All.

问题是我有循环引用.

这是一个非常简单的例子.

Here is a very simple example.

class Node
{
    public Node(object value)
    {
        Value = value;
    }
    public object Value { get; set; }
    public Node Left { get; set; }
    public Node Right { get; set; }
}

我的测试场景是:

var obj = new Node("o")
{
    Left = new Node("oL"),
    Right = new Node("oR")
};
obj.Right.Right = obj; // Circular reference!

反序列化时,我有以下IReferenceResolver

When I deserialize, i have the following IReferenceResolver

private class InternalReferenceResolver : IReferenceResolver
{
    private readonly Deserializer _par;

    public InternalReferenceResolver(Deserializer par)
    {
        _par = par;
    }

    public object ResolveReference(object context, string reference)
    {
        object refObject;
        if (!_par._processed.TryGetValue(reference, out refObject))
        {
            refObject = _par.DeserializeObject(reference);
        }
        return refObject;
    }

    public string GetReference(object context, object value)
    {
        throw new NotSupportedException("Only for Serialization");
    }

    public bool IsReferenced(object context, object value)
    {
        return false;
    }

    public void AddReference(object context, string reference, object value)
    {
        _par._processed.Add(reference, value);
    }
}    

如您所见,当JSON.NET通知我一个新的ref->对象(通过AddReference())时,我将其添加到字典中.

As you can see, when JSON.NET inform me of a new ref->object (via AddReference()) I add it to a dictionary.

当JSON.NET向对象请求特定引用(通过ResolveReference())时,我递归并反序列化该引用.

When JSON.NET request an object for a specific reference (via ResolveReference()) I recurse, and deserialize that reference.

问题在于,JSON.NET在调用对象的AddReference()之前先对其每个对象引用调用ResolveReference().

The issue is, JSON.NET calls ResolveReference() for each of the object references, before it calls it's AddReference().

我希望反序列化的流程为:

I would expect the flow of Deserialization to be:

  1. 识别对象类型
  2. 构造对象
  3. AddReference(id,newObj)
  4. 解析引用+填充属性

我看到的是:

  1. 识别对象类型
  2. 解析引用
  3. 构造对象
  4. AddReference(id,newObj)
  5. 填充属性

我的问题:

  1. 为什么要选择后者,我的建议流程中是否缺少某些东西?

  1. Why is it made the latter, am i missing something with my suggested flow?

我如何克服这个问题,只有一个裸"对象用于引用,然后才能真正解析引用?

how can I overcome this issue, having a "Bare" object just for referencing and only then actually resolve the references ?

推荐答案

出现的两阶段反序列化是因为您的Node类仅具有参数化构造函数而引起的.如:

The Two-Phase deserialization you are seeing is arises because your Node class only has a parameterized constructor. As explained in Issue with serializing/deserializing object graph with self-referencing objects in combination with JSON constructor. #715:

JamesNK 于2015年11月28日发表评论

JamesNK commented on Nov 28, 2015

非默认构造函数和保留引用不能很好地协同工作,因为类型的子值必须在创建父类之前先反序列化(),因此引用解析为null.

Non-default constructors and preserving references don't work well together because the child values of a type have to be deserialized before the parent is created, so the reference resolves to null.

因此,由于Value仍然是可变属性,因此您应该向Node添加无参数构造函数:

Thus, since Value is a mutable property anyway, you should add a parameterless constructor to Node:

class Node
{
    public Node() : this(null) { }

    public Node(object value)
    {
        Value = value;
    }
    // Remainder unchanged.
}

如果您将其标记为 [JsonConstructor] 或使用设置 ConstructorHandling.AllowNonPublicDefaultConstructor 反序列化.而且,如果Value是不可变的,则需要将其设置为可私有设置并用

It can be non-public if you mark it with [JsonConstructor] or deserialize using the setting ConstructorHandling.AllowNonPublicDefaultConstructor. And if Value were immutable, you would need to make it privately settable and mark it with [JsonProperty]

[JsonProperty]
public object Value { get; private set; }

( ="数据合同属性可以代替Json使用.NET属性(如果您愿意).

(Data contract attributes can be used in place of Json.NET attributes if you prefer.)

注意:

  • 由于您的问题缺少完整且可验证的示例,因此您的代码可能会遇到其他无法解决的问题添加无参数的构造函数.

  • Since your question lacks a complete and verifiable example, your code might encounter other problems that aren't fixed by adding a parameterless constructor.

有关相关问题,请参见使用非默认构造函数会破坏Json.net中反序列化的顺序.

For a related question, see Usage of non-default constructor breaks order of deserialization in Json.net.

有关相关问题,请参见 PreserveReferencesHandling.Object反序列化不适用于非默认构造函数#678 .

For a related issue, see PreserveReferencesHandling.Objects deserialize does not work with non-default constructor #678.

另一件事,我可以告诉Json.NET仅处理构造函数参数,而保留其余的...吗?

不符合问题715.由于 JSON 对象是无序的名称集/值对,Json.NET需要解析整个对象以确保它已加载所有构造函数参数.由于它是单遍序列化程序,因此在构造对象之前,非构造函数参数将被加载到某些东西中. Json.NET已选择一步将其反序列化为最终目标成员类型,而不是中间的JToken以及后来的最终成员类型.可以在> JsonSerializerInternalReader.ResolvePropertyAndCreatorValues() .

Not according to Issue 715. Since a JSON object is an unordered set of name/value pairs, Json.NET needs to parse the entire object to ensure it has loaded all the constructor parameters. As it is a single-pass serializer the non-constructor parameters will get loaded into... something... before the object can be constructed. Json.NET has chosen to deserialize them in one step to the final target member type rather than to an intermediate JToken and later the final member type. This can be see in JsonSerializerInternalReader.ResolvePropertyAndCreatorValues().

这篇关于通过两阶段反序列化反序列化循环引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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