C# 通过两阶段反序列化对循环引用进行反序列化

C# 通过两阶段反序列化对循环引用进行反序列化,c#,json,serialization,json.net,C#,Json,Serialization,Json.net,我有一个使用referencespreserverencesshandling=preserverencesshandling.All的序列化程序/反序列化程序 问题是,我有循环引用 这是一个非常简单的例子 class Node { public Node(object value) { Value = value; } public object Value { get; set; } public Node Left { get; se

我有一个使用references
preserverencesshandling=preserverencesshandling.All
的序列化程序/反序列化程序

问题是,我有循环引用

这是一个非常简单的例子

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

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);
    }
}    
    private class InternalOnlyCtorContractResolver : IContractResolver
    {
        private readonly IContractResolver _base;

        public InternalOnlyCtorContractResolver(IContractResolver _base)
        {
            this._base = _base;
        }

        public JsonContract ResolveContract(Type type)
        {
            var contract = _base.ResolveContract(type);
            var objectContract = contract as JsonObjectContract;
            if (objectContract != null)
            {
                var creatorParameters = new HashSet<string>(objectContract.CreatorParameters.Select(p => p.PropertyName));
                var irrelevantProperties = objectContract.Properties
                    .Where(p => !creatorParameters.Contains(p.PropertyName))
                    .ToArray();
                foreach (var irrelevantProperty in irrelevantProperties)
                {
                    objectContract.Properties.Remove(irrelevantProperty);
                }
                //TODO Can be optimized better
            }
            return contract;
        }
    }
如您所见,当JSON.NET通知我一个新的ref->object(通过
AddReference()
)时,我将其添加到字典中

当JSON.NET请求一个对象作为特定引用时(通过
resolverence()
),我递归并反序列化该引用

问题是,JSON.NET在调用其
AddReference()
之前,会对每个对象引用调用
resolverence()

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

  • 标识对象类型
  • 构造对象
  • AddReference(id,newObj)
  • 解析引用+填充属性
  • 我看到的是:

  • 标识对象类型
  • 解析引用
  • 构造对象
  • AddReference(id,newObj)
  • 填充属性
  • 我的问题是:

  • 为什么是后者,我的建议流程是否遗漏了什么

  • 我如何克服这个问题,拥有一个“裸”对象只是为了引用,然后才真正解析引用

  • 您看到的两阶段反序列化是因为
    节点
    类只有一个参数化构造函数。如中所述:

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

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

    因此,由于
    无论如何都是一个可变属性,因此应该向
    节点
    添加一个无参数构造函数:

    class Node
    {
        public Node() : this(null) { }
    
        public Node(object value)
        {
            Value = value;
        }
        // Remainder unchanged.
    }
    
    如果使用设置对其进行标记或反序列化,则它可能是非公共的。如果
    Value
    是不可变的,则需要将其设置为私有的,并用

    (如果愿意,可以用它代替Json.NET属性。)

    注:

    • 由于您的问题缺少参数,您的代码可能会遇到其他无法通过添加无参数构造函数解决的问题

    • 有关相关问题,请参阅

    • 有关相关问题,请参阅

    • 另一件事,我能告诉Json.NET只处理构造函数参数而不处理其余的

      不符合第715期。由于对象是一组无序的名称/值对,Json.NET需要解析整个对象,以确保它已加载所有构造函数参数。由于它是单通道序列化程序,非构造函数参数将加载到。。。某物在构建对象之前。Json.NET选择一步将它们反序列化为最终的目标成员类型,而不是中间
      JToken
      和最终的成员类型。这可以从中看出

      • 您看到的两阶段反序列化之所以出现,是因为您的
        节点
        类只有一个参数化构造函数。如中所述:

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

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

        因此,由于
        无论如何都是一个可变属性,因此应该向
        节点
        添加一个无参数构造函数:

        class Node
        {
            public Node() : this(null) { }
        
            public Node(object value)
            {
                Value = value;
            }
            // Remainder unchanged.
        }
        
        如果使用设置对其进行标记或反序列化,则它可能是非公共的。如果
        Value
        是不可变的,则需要将其设置为私有的,并用

        (如果愿意,可以用它代替Json.NET属性。)

        注:

        • 由于您的问题缺少参数,您的代码可能会遇到其他无法通过添加无参数构造函数解决的问题

        • 有关相关问题,请参阅

        • 有关相关问题,请参阅

        • 另一件事,我能告诉Json.NET只处理构造函数参数而不处理其余的

          不符合第715期。由于对象是一组无序的名称/值对,Json.NET需要解析整个对象,以确保它已加载所有构造函数参数。由于它是单通道序列化程序,非构造函数参数将加载到。。。某物在构建对象之前。Json.NET选择一步将它们反序列化为最终的目标成员类型,而不是中间
          JToken
          和最终的成员类型。这可以从中看出


        我找到了解决问题的方法:

        在我执行的第一次反序列化中,我使用一个自定义的
        IContractResolver
        ,它排除了与构造函数无关的所有属性

        在第二步中,我使用Populate并使用默认的
        IContractResolver

        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);
            }
        }    
        
            private class InternalOnlyCtorContractResolver : IContractResolver
            {
                private readonly IContractResolver _base;
        
                public InternalOnlyCtorContractResolver(IContractResolver _base)
                {
                    this._base = _base;
                }
        
                public JsonContract ResolveContract(Type type)
                {
                    var contract = _base.ResolveContract(type);
                    var objectContract = contract as JsonObjectContract;
                    if (objectContract != null)
                    {
                        var creatorParameters = new HashSet<string>(objectContract.CreatorParameters.Select(p => p.PropertyName));
                        var irrelevantProperties = objectContract.Properties
                            .Where(p => !creatorParameters.Contains(p.PropertyName))
                            .ToArray();
                        foreach (var irrelevantProperty in irrelevantProperties)
                        {
                            objectContract.Properties.Remove(irrelevantProperty);
                        }
                        //TODO Can be optimized better
                    }
                    return contract;
                }
            }
        
        私有类InternalOnlyTorContractResolver:IContractResolver
        {
        专用只读IContractResolver_base;
        public internalonlytorContractResolver(IContractResolver\u base)
        {
        这个。_base=_base;
        }
        公共JsonContract ResolveContract(类型)
        {
        var合同=_base.ResolveContract(类型);
        var objectContract=作为JsonObjectContract的合同;
        if(objectContract!=null)
        {
        var creatorParameters=newhashset(objectContract.creatorParameters.Select(p=>p.PropertyName));
        var irrelationproperties=objectContract.Properties
        .Where(p=>!creatorParameters.Contains(p。