C# XML序列化器 - 序列化派生对象

3

我想要序列化以下内容:

[Serializable]
[DefaultPropertyAttribute("Name")]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))] 
public class ItemInfo
{
    public string name;

    [XmlArray("Items"), XmlArrayItem(typeof(ItemInfo))]
    public ArrayList arr;

    public ItemInfo parentItemInfo;
}

[Serializable]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))] 
public class ItemInfoA : ItemInfo
{
...
}

[Serializable]
[XmlInclude(typeof(ItemInfo))]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))] 
public class ItemInfoB : ItemInfo
{
...
}

itemInfo描述了一个容器,可以在数组列表中容纳其他itemInfo对象,parentItemInfo描述了项目信息的父容器。

由于ItemInfoAItemInfoB派生自ItemInfo,它们也可以成为数组列表和parentItemInfo的成员,因此当尝试序列化这些对象(可以在层次结构中容纳许多对象)时,会出现异常。

IvvalidOperationException.`there was an error generating the xml file `

我的问题是:

我需要为ItemInfo类添加哪些属性才能使其可序列化?

注意:只有在使用parentItemInfo或arrayList初始化ItemInfo[A]/[B]时才会出现异常。

请帮忙解答!

谢谢!


1
私有成员不会随 XmlSerializer 进行序列化,因此在此情况下 arr 永远 不会被序列化, parentItemInfo 也一样。我认为我们需要一个可重现的示例... 另外,虽然不重要,但 XmlSerializer 没有使用 [Serializable] - Marc Gravell
1
哦,为什么在这里使用ArrayList?你是在1.1吗? - Marc Gravell
此外,“ItemInfoA”或“ItemInfoB”上的任何属性都没有在这里使用... - Marc Gravell
Marc Gravell - 抱歉,请查看我编辑过的问题 - gln
1个回答

8

通过修改问题,看起来你有一个循环。请注意,XmlSerializer 是一种树形序列化器,而不是图形序列化器,因此它将失败。通常的解决方法是禁用向上遍历:

[XmlIgnore]
public ItemInfo parentItemInfo;

请注意,在反序列化后,您需要手动修复父级对象。
关于异常 - 您需要查看InnerException - 它可能会告诉您确切的信息,例如在您的(catch ex)中:
while(ex != null) {
    Debug.WriteLine(ex.Message);
    ex = ex.InnerException;
}

我猜测实际上是:

"在序列化类型为ItemInfoA的对象时检测到循环引用。"


更一般地说,公共字段、ArrayList和可设置列表都是不良实践;以下是一个行为完全相同的更典型的重写:
[DefaultPropertyAttribute("Name")]
[XmlInclude(typeof(ItemInfoA))]
[XmlInclude(typeof(ItemInfoB))] 
public class ItemInfo
{
    [XmlElement("name")]
    public string Name { get; set; }

    private readonly List<ItemInfo> items = new List<ItemInfo>();
    public List<ItemInfo> Items { get { return items; } }

    [XmlIgnore]
    public ItemInfo ParentItemInfo { get; set; }
}
public class ItemInfoA : ItemInfo
{
}
public class ItemInfoB : ItemInfo
{
}

根据要求,这里是一个通用的(不具体针对问题)“递归设置Hive父项”的示例(为了好玩,我使用了深度优先堆的方法;如果采用广度优先,则可以将Stack<T>替换为Queue<T>;在这些场景中,我尝试避免基于堆栈的递归):

public static void SetParentsRecursive(Item parent)
{
    List<Item> done = new List<Item>();
    Stack<Item> pending = new Stack<Item>();
    pending.Push(parent);
    while(pending.Count > 0)
    {
        parent = pending.Pop();
        foreach(var child in parent.Items)
        {
            if(!done.Contains(child))
            {
                child.Parent = parent;
                done.Add(child);
                pending.Push(child);
            }                
        }
    }
}

我该如何进行反序列化? - gln
@gln 将其正常反序列化,但您必须 * 要么 * 遍历树并分配父项,要么编写自定义列表实现来为您执行此操作。 - Marc Gravell
@gln - 真的吗?那是一个非常简单的递归代码块... 我稍后会添加示例(现在不在电脑旁边),但是... - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接