Stackoverflow序列化类时的异常

时间:2014-10-26 16:41:57

标签: c# exception xmlserializer

我有一棵树,想要将它们序列化为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();
}

1 个答案:

答案 0 :(得分:8)

您遇到XmlSerializer的多个问题。

首先,XmlSerializer区分序列化集合和常规对象。序列化集合时,只序列化集合中的项目,而不是集合类本身的属性。否则,如果类不是集合,则将序列化属性。这在documentation

中有详细说明
  

可以序列化的项目

     

可以使用XmLSerializer类序列化以下项目:

     
      
  • 公共读/写属性和公共类的字段。

  •   
  • 实现ICollection或IEnumerable的类。

         

    注意:的   仅序列化集合,而不是公共属性。

  •   
  • XmlElement对象。

  •   
  • XmlNode对象。

  •   
  • DataSet对象。

  •   

您的NodeBase类同时作为节点和IEnumerable子节点运行。因此,XmlSerializer不会序列化派生类的任何属性,这可能不是您想要的。相反,您需要提取单独的Children属性,并且仅枚举&amp;序列化使用它。

(顺便说一句,使NodeBase实现IEnumerable<NodeBase>以某种方式导致XmlSerializer的构造函数溢出堆栈。这让我感到惊讶 - 但即使这没有发生,您的代码无法按预期工作。)

其次,即使您通过子属性序列化子项,您将遇到另一个无限递归。这是因为XmlSerializer树序列化程序而不是图形序列化程序。区别如下:

  1. 图形序列化程序(例如BinaryFormatter)从序列化的根对象开始递归地下降对象图。它第一次遇到一个对象时,它在一个表中序列化它,为它生成一个临时ID,并序列化容器类中的ID。如果序列化程序随后遇到相同的对象,它会在表中查找并再次存储运行时ID。

    因此,可以序列化多次引用节点的循环对象图和图形。

  2. XmlSerializer树序列化程序更受限制。它从被序列化的根对象开始递归地下降对象图,并在遇到时序列化每个对象。如果它遇到相同的对象两次,它将序列化两次。如果它在对象图中遇到一个循环,它将落入无限递归。这是因为它期望并要求对象层次结构为纯tree

  3. 所以,在你的结构中,你有:

    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>。)