C# 如何使用自定义添加方法进行集合反序列化过程?

C# 如何使用自定义添加方法进行集合反序列化过程?,c#,.net,serialization,deserialization,encapsulation,C#,.net,Serialization,Deserialization,Encapsulation,我有一个树状节点结构的类。它具有用于隐藏直接更改子对象的只读集合类型的Children属性和用于控制子对象添加的AddChild(…)方法 class TreeNode { List<TreeNode> _children = new List<TreeNode>(); public IReadOnlyList<TreeNode> Children => children; public string Name { get; set;

我有一个树状节点结构的类。它具有用于隐藏直接更改子对象的只读集合类型的Children属性和用于控制子对象添加的AddChild(…)方法

class TreeNode {
   List<TreeNode> _children = new List<TreeNode>();
   public IReadOnlyList<TreeNode> Children => children;
   public string Name { get; set; } // some other filed 
   public void AddChild(TreeNode node){
      // ... some code
      _children.Add(node);
   }
}
类树节点{
列表_children=新列表();
公共IReadOnlyList Children=>Children;
公共字符串名称{get;set;}//其他一些字段
公共void AddChild(TreeNode节点){
//…一些代码
_添加(节点);
}
}
我需要为我的类提供反序列化。我试过:

[Serializable]
[XmlRoot(ElementName = "node")]
class TreeNode {
   List<TreeNode> _children = new List<TreeNode>();

   [XmlElement(ElementName = "node")]
   public IReadOnlyList<TreeNode> Children => children;

   [XmlAttribute(DataType = "string", AttributeName = "name")]
   public string Name { get; set; } // some other filed 

   public void AddChild(TreeNode node){
      // ... some code
      _children.Add(node);
   }

   public static TreeNode Deserialize(Stream stream) {
        var serializer = new XmlSerializer(typeof(TreeNode));
        var obj = serializer.Deserialize(stream);
        var tree = (TreeNode)obj;
        return tree;
   }
}
[可序列化]
[XmlRoot(ElementName=“node”)]
三烯类{
列表_children=新列表();
[xmlement(ElementName=“node”)]
公共IReadOnlyList Children=>Children;
[XmlAttribute(DataType=“string”,AttributeName=“name”)]
公共字符串名称{get;set;}//其他一些字段
公共void AddChild(TreeNode节点){
//…一些代码
_添加(节点);
}
公共静态树节点反序列化(流){
var serializer=newxmlserializer(typeof(TreeNode));
var obj=序列化程序。反序列化(流);
var-tree=(TreeNode)obj;
回归树;
}
}
当然,这不起作用,因为IReadOnlyList没有Add方法

是否可以将AddChild绑定到反序列化进程?如果“是”,怎么做


如何提供具有反序列化功能的相同封装级别?

如果这是
XmlSerializer
,那么:不,除非您完全实现
IXmlSerializable
,否则您无法做到这一点,这很难正确实现,并且首先会破坏使用
XmlSerializer
的全部目的


如果数据不是很大,那么对于“我现有的对象模型与我选择的序列化程序不兼容”这种形式的任何问题,我的默认答案是:当它变得混乱时,停止序列化现有的对象模型。相反,创建一个单独的DTO模型,该模型专门设计为与所选序列化程序配合使用,并在序列化之前将数据映射到DTO模型中,然后再映射回来。这可能意味着在DTO模型中使用
List
而不是
IReadOnlyList

这可以通过向
TreeNode
添加代理属性来实现,该属性返回一个代理包装类型,该类型使用提供给其构造函数的委托来实现
IEnumerable
Add(T)
。首先,介绍以下代理包装器:

// Proxy class for any enumerable with the requisite `Add` methods.
public class EnumerableProxy<T> : IEnumerable<T>
{
    readonly Action<T> add;
    readonly Func<IEnumerable<T>> getEnumerable;

    // XmlSerializer required default constructor (which can be private).
    EnumerableProxy()
    {
        throw new NotImplementedException("The parameterless constructor should never be called directly");
    }

    public EnumerableProxy(Func<IEnumerable<T>> getEnumerable, Action<T> add)
    {
        if (getEnumerable == null || add == null)
            throw new ArgumentNullException();
        this.getEnumerable = getEnumerable;
        this.add = add;
    }

    public void Add(T obj)
    {
        // Required Add() method as documented here:
        // https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.100%29.aspx
        add(obj);
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return (getEnumerable() ?? Enumerable.Empty<T>()).GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}
您的类型现在可以通过
XmlSerializer
完全序列化和反序列化。工作

此解决方案利用了
XmlSerializer
的以下记录行为。首先,如以下文件所述:

XmlSerializer对实现IEnumerable或ICollection的类给予特殊处理。实现IEnumerable的类必须实现接受单个参数的公共方法。
Add
方法的参数的类型必须与从
返回的值的
属性返回的参数的类型相同,或者是该类型的基之一

因此,您的代理
IEnumerable
包装器实际上不需要使用其完整的方法集实现,包括,等等。只要一个签名正确的
Add()
,就足够了。(如果您想为Json.NET实现类似的解决方案,您的代理类型将需要实现
ICollection
——但是您可以从不必要的方法中抛出异常,例如
Remove()
Clear()

第二,如以下文件所述:

XML序列化不会转换方法、索引器、专用字段或只读属性(只读集合除外)


也就是说,
XmlSerializer
可以成功地反序列化预先分配的集合中的项,即使该集合是由get-only属性返回的。这避免了为代理项属性实现
set
方法或为代理项集合包装类型实现默认构造函数的需要。

为什么它是
ReadOnlyList
?为什么不把它变成一个
私有列表
,然后公开可以添加到其中的
AddChild
方法呢?您应该查看
ISerializable
界面@CihanYakar
XmlSerializer
不关心
ISerializable
,而且
IXmlSerializable
很难正确实现
[XmlRoot(ElementName = "node")]
public class TreeNode
{
    List<TreeNode> _children = new List<TreeNode>();

    [XmlIgnore]
    public IReadOnlyList<TreeNode> Children { get { return _children; } }

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    [XmlElement(ElementName = "node")]
    public EnumerableProxy<TreeNode> ChildrenSurrogate
    {
        get
        {
            return new EnumerableProxy<TreeNode>(() => _children, n => AddChild(n));
        }
    }

    [XmlAttribute(DataType = "string", AttributeName = "name")]
    public string Name { get; set; } // some other filed 

    public void AddChild(TreeNode node)
    {
        // ... some code
        _children.Add(node);
    }
}