C# 如何使用自定义添加方法进行集合反序列化过程?
我有一个树状节点结构的类。它具有用于隐藏直接更改子对象的只读集合类型的Children属性和用于控制子对象添加的AddChild(…)方法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;
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
界面@CihanYakarXmlSerializer
不关心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);
}
}