简体   繁体   中英

C# xml serializer - serialize derived objects

I want to serialize the following:

[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
{
...
}

The class itemInfo describes a container which can hold other itemInfo objects in the array list, the parentItemInfo describes which is the parent container of the item info.

Since ItemInfoA and ItemInfoB derive from ItemInfo they can also be a member of the array list and the parentItemInfo , therefore when trying to serialize these objects (which can hold many objects in hierarchy) it fails with exception

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

My question is:

What attributes do I need to add the ItemInfo class so it will be serializable?

Note: the exception is only when the ItemInfo[A]/[B] are initialized with parentItemInfo or the arrayList.

Help please!

Thanks!

With the edited question, it looks like you have a loop. Note that XmlSerializer is a tree serializer, not a graph serializer, so it will fail. The usual fix here is to disable upwards traversal:

[XmlIgnore]
public ItemInfo parentItemInfo;

Note you will have to manually fixup the parents after deserialization, of course.

Re the exception - you need to look at the InnerException - it probably tells you exactly this, for example in your (catch ex) :

while(ex != null) {
    Debug.WriteLine(ex.Message);
    ex = ex.InnerException;
}

I'm guessing it is actually:

"A circular reference was detected while serializing an object of type ItemInfoA."


More generally on the design, honestly that (public fields, ArrayList , settable lists) is bad practice; here's a more typical re-write that behaves identically :

[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
{
}

as requested, here's a general (not question-specific) illustration of recursively setting the parents in a hive (for kicks I'm using depth-first on the heap; for bredth-first just swap Stack<T> for Queue<T> ; I try to avoid stack-based recursion in these scenarios):

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);
            }                
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM