我有一棵树,想要将它们序列化为xml。节点派生自Nodebase类(我认为在这里找到),序列化失败。
public class NodeBase : IEqualityComparer, IEnumerable, IEnumerable<NodeBase>
{
public NodeBase Parent { get; private set; }
private readonly IList<NodeBase> children = new ObservableCollection<NodeBase>();
public NodeBase this[int index]
{
get
{
return this.children[index];
}
}
public void AddChild(NodeBase childNode, int index = -1)
{
if (index < -1)
{
throw new ArgumentException("The index can not be lower then -1");
}
if (index > this.Children.Count() - 1)
{
throw new ArgumentException("The index ({0}) can not be higher then index of the last iten. Use the AddChild() method without an index to add at the end".FormatInvariant(index));
}
if (!childNode.IsRoot)
{
throw new ArgumentException("The child node with value [{0}] can not be added because it is not a root node.".FormatInvariant(childNode.ToString()));
}
if (this.Root == childNode)
{
throw new ArgumentException("The child node with value [{0}] is the rootnode of the parent.".FormatInvariant(childNode.ToString()));
}
if (childNode.SelfAndDescendants.Any(n => this == n))
{
throw new ArgumentException("The childnode with value [{0}] can not be added to itself or its descendants.".FormatInvariant(childNode.ToString()));
}
childNode.Parent = this;
if (index == -1)
{
this.children.Add(childNode);
}
else
{
this.children.Insert(index, childNode);
}
}
public void AddChildren(params NodeBase[] childNodes)
{
foreach (var childNode in childNodes)
{
this.AddChild(childNode);
}
}
public bool RemoveChild(NodeBase node)
{
return this.children.Remove(node);
}
public void AddFirstChild(NodeBase childNode)
{
this.AddChild(childNode, 0);
}
public void AddFirstSibling(NodeBase childNode)
{
this.Parent.AddFirstChild(childNode);
}
public void AddLastSibling(NodeBase childNode)
{
this.Parent.AddChild(childNode);
}
public IEnumerable<NodeBase> Leaves
{
get
{
return this.Descendants.Where(n => !n.Children.Any());
}
}
public void AddParent(NodeBase parentNode)
{
if (!this.IsRoot)
{
throw new ArgumentException("This node [{0}] already has a parent".FormatInvariant(this.ToString()), "parentNode");
}
parentNode.AddChild(this);
}
public IEnumerable<NodeBase> Ancestors
{
get
{
if (this.IsRoot)
{
return Enumerable.Empty<NodeBase>();
}
return this.Parent.ToIEnumerable().Concat(this.Parent.Ancestors);
}
}
public IEnumerable<NodeBase> Descendants
{
get
{
return this.SelfAndDescendants.Skip(1);
}
}
public IEnumerable<NodeBase> Children
{
get
{
return this.children;
}
}
public IEnumerable<NodeBase> Siblings
{
get
{
return this.SelfAndSiblings.Where(Other);
}
}
private bool Other(NodeBase node)
{
return !ReferenceEquals(node, this);
}
public IEnumerable<NodeBase> SelfAndChildren
{
get
{
return this.ToIEnumerable().Concat(Children);
}
}
public IEnumerable<NodeBase> SelfAndAncestors
{
get
{
return this.ToIEnumerable().Concat(Ancestors);
}
}
public IEnumerable<NodeBase> SelfAndDescendants
{
get
{
return this.ToIEnumerable().Concat(this.Children.SelectMany(c => c.SelfAndDescendants));
}
}
public IEnumerable<NodeBase> SelfAndSiblings
{
get
{
if (this.IsRoot)
{
return this.ToIEnumerable();
}
return this.Parent.Children;
}
}
public NodeBase GetPreviousSibling()
{
return this.GetPreviousSibling(this);
}
public NodeBase GetPreviousSibling(NodeBase node)
{
if (this.Parent == null)
{
return null;
}
var previousNode = this.Parent.Children.Reverse().SkipWhile(i => !i.Equals(node))
.Skip(1)
.FirstOrDefault();
return previousNode;
}
public NodeBase GetPreviousNode()
{
var previousSibling = this.GetPreviousSibling();
if (previousSibling != null)
{
if (this.HasChildren)
{
NodeBase current = this;
while (true)
{
var child = current.Children.Last();
if (!child.HasChildren)
{
return child;
}
else
{
current = child;
}
}
}
else
{
return previousSibling;
}
}
else
{
if (this.HasParent)
{
return this.Parent;
}
else
{
return null;
}
}
}
public NodeBase GetNextNode()
{
if (this.HasChildren)
{
return this.Children.First();
}
else
{
var nextSibling = this.GetNextSibling();
if (nextSibling != null)
{
return nextSibling;
}
else
{
NodeBase current = this;
NodeBase parent;
while (true)
{
parent = current.Parent;
if (parent == null)
return null;
else
{
var nextSibling2 = parent.GetNextSibling();
if (nextSibling2 != null)
{
return nextSibling2;
}
else
{
current = parent;
}
}
}
}
}
}
public bool HasParent
{
get { return this.Parent != null; }
}
public bool HasChildren
{
get
{
return this.children.Any();
}
}
public NodeBase GetNextSibling()
{
return this.GetNextSibling(this);
}
public NodeBase GetNextSibling(NodeBase node)
{
if (this.Parent == null)
{
return null;
}
var foundNode = this.Parent.Children.SkipWhile(i => !i.Equals(node));
var nextNode = foundNode.Skip(1)
.FirstOrDefault();
return nextNode;
}
public IEnumerable<NodeBase> All
{
get
{
return this.Root.SelfAndDescendants;
}
}
public IEnumerable<NodeBase> SameLevel
{
get
{
return this.SelfAndSameLevel.Where(Other);
}
}
public int Level
{
get
{
return this.Ancestors.Count();
}
}
public IEnumerable<NodeBase> SelfAndSameLevel
{
get
{
return this.GetNodesAtLevel(Level);
}
}
public IEnumerable<NodeBase> GetNodesAtLevel(int level)
{
return this.Root.GetNodesAtLevelInternal(level);
}
private IEnumerable<NodeBase> GetNodesAtLevelInternal(int level)
{
if (level == this.Level)
{
return this.ToIEnumerable();
}
return this.Children.SelectMany(c => c.GetNodesAtLevelInternal(level));
}
public NodeBase Root
{
get
{
return this.SelfAndAncestors.Last();
}
}
public void Disconnect()
{
if (this.IsRoot)
{
throw new InvalidOperationException("The root node [{0}] can not get disconnected from a parent.".FormatInvariant(this.ToString()));
}
this.Parent.children.Remove(this);
this.Parent = null;
}
public bool IsRoot
{
get
{
return this.Parent == null;
}
}
public void Traverse(Action<NodeBase> action)
{
action(this);
foreach (var child in children)
{
child.Traverse(action);
}
}
public IEnumerable<NodeBase> Flatten()
{
return new[] { this }.Union(children.SelectMany(x => x.Flatten()));
}
IEnumerator<NodeBase> IEnumerable<NodeBase>.GetEnumerator()
{
return this.children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.children.GetEnumerator();
}
public IEnumerator<NodeBase> GetEnumerator()
{
return this.children.GetEnumerator();
}
private static bool IsSameId<TId>(TId id, TId? parentId)
where TId : struct
{
return parentId != null && id.Equals(parentId.Value);
}
#region Equals en ==
public static bool operator ==(NodeBase value1, NodeBase value2)
{
if ((object)(value1) == null && (object)value2 == null)
{
return true;
}
return ReferenceEquals(value1, value2);
}
public static bool operator !=(NodeBase value1, NodeBase value2)
{
return !(value1 == value2);
}
public override bool Equals(Object anderePeriode)
{
var valueThisType = anderePeriode as NodeBase;
return this == valueThisType;
}
public bool Equals(NodeBase value)
{
return this == value;
}
public bool Equals(NodeBase value1, NodeBase value2)
{
return value1 == value2;
}
bool IEqualityComparer.Equals(object value1, object value2)
{
var valueThisType1 = value1 as NodeBase;
var valueThisType2 = value2 as NodeBase;
return Equals(valueThisType1, valueThisType2);
}
public int GetHashCode(object obj)
{
return GetHashCode(obj as NodeBase);
}
public override int GetHashCode()
{
return GetHashCode(this);
}
public int GetHashCode(NodeBase value)
{
return base.GetHashCode();
}
#endregion Equals en ==
}
首先,当存在函数Add(System.Object)时,序列化程序建议只能序列化IEnumerable。为什么呢?
我添加了一个虚拟函数 public void Add(对象节点) { }
并尝试序列化。我得到一个Stackoverflow异常。 为什么,这堂课没什么特别之处。我做错了什么?
public string SerializeToString<T>(T objectInstance)
{
var xmlSerializer = new XmlSerializer(typeof(T));
var xml = new StringBuilder();
using (TextWriter writer = new StringWriter(xml))
{
xmlSerializer.Serialize(writer, objectInstance);
}
return xml.ToString();
}
答案 0 :(得分:8)
您遇到XmlSerializer
的多个问题。
首先,XmlSerializer
区分序列化集合和常规对象。序列化集合时,只序列化集合中的项目,而不是集合类本身的属性。否则,如果类不是集合,则将序列化属性。这在documentation:
可以序列化的项目
可以使用XmLSerializer类序列化以下项目:
公共读/写属性和公共类的字段。
实现ICollection或IEnumerable的类。
注意:的 仅序列化集合,而不是公共属性。
XmlElement对象。
XmlNode对象。
DataSet对象。
您的NodeBase
类同时作为节点和IEnumerable子节点运行。因此,XmlSerializer
不会序列化派生类的任何属性,这可能不是您想要的。相反,您需要提取单独的Children
属性,并且仅枚举&amp;序列化使用它。
(顺便说一句,使NodeBase
实现IEnumerable<NodeBase>
以某种方式导致XmlSerializer
的构造函数溢出堆栈。这让我感到惊讶 - 但即使这没有发生,您的代码无法按预期工作。)
其次,即使您通过子属性序列化子项,您将遇到另一个无限递归。这是因为XmlSerializer
是树序列化程序而不是图形序列化程序。区别如下:
图形序列化程序(例如BinaryFormatter
)从序列化的根对象开始递归地下降对象图。它第一次遇到一个对象时,它在一个表中序列化它,为它生成一个临时ID,并序列化容器类中的ID。如果序列化程序随后遇到相同的对象,它会在表中查找并再次存储运行时ID。
因此,可以序列化多次引用节点的循环对象图和图形。
XmlSerializer
等树序列化程序更受限制。它从被序列化的根对象开始递归地下降对象图,并在遇到时序列化每个对象。如果它遇到相同的对象两次,它将序列化两次。如果它在对象图中遇到一个循环,它将落入无限递归。这是因为它期望并要求对象层次结构为纯tree。
所以,在你的结构中,你有:
public class NodeBase
{
public NodeBase Parent { get; private set; }
public IEnumerable<NodeBase> Children
{
get
{
return this.children;
}
}
}
这两个属性都是公共的,因此可以序列化。因此,根节点将递归序列化其第一个子节点,Parent
属性将递归序列化父。
要解决此问题,请将NodeBase
以外的所有Children
相关属性标记为[XmlIgnore]
。您还需要使用代理属性将子项显式序列化为数组:
public class NodeBase
{
[XmlIgnore]
public NodeBase Parent { get; private set; }
[XmlIgnore]
public IEnumerable<NodeBase> Children
{
get
{
return this.children;
}
}
[XmlArray("Children"), Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public NodeBase [] ChildList
{
get
{
return children.ToArray();
}
set
{
if (!object.ReferenceEquals(value, this.children))
{
children.Clear();
foreach (var child in value)
AddChild(child);
}
}
}
}
这将允许您的树被序列化和反序列化。
(顺便说一下,使类T
实现IEqualityComparer<T>
非常不典型。通常它实现IEquatable<T>
和/或一些单独的比较器类实现IEqualityComparer<T>
。)