Using an XML to (parse?) elements in multiple different classes C# - c#

I'm truly sorry if the answer exist here under another name but I've been trying to figure this out for a week or so and haven't found anything similar.
So, I'm building an Item system for a game in unity. There is an Item class with these properties
public int ItemId;
public string ItemName;
For simplicity, let's say I want to have two derived classes, Weapon and Armor, with a BaseDamage property for Weapon and BaseArmor for Armor.
I also want to load the items in an ItemContainer class from XML and ideally, from the same XML file rather than one for the weapons, one for armors and one for, say, potions.
I've tried multiple ways but so far the only way I've been able to succeed is by adding the BaseDamage and BaseArmor to the Item base class... like this :
public class Item
{
[XmlAttribute("ID")] public int ItemID;
[XmlElement("Name")] public string ItemName;
[XmlElement("Damage")] public int ItemBaseDamage;
[XmlElement("Armor")] public int ItemBaseArmor;
}
and simply not adding the element to the XML file for some items :
<ItemCollection>
<Items>
<Item ID ="001">
<Name>Dagger</Name>
<Damage>10</Damage>
</Item>
<Item ID ="002">
<Name>Chain Mail</Name>
<Armor>5</Armor>
</Item>
</Items>
</ItemCollection>
It does work, but I feel like this isn't the correct way to do it. Another issue is that if I want to add a Scroll class with a certain function to cast the spell written on that scroll, I need to add the "SpellToCast" property to the base class AND add a CastSpell(Spell) function to it that could be called from any item, which is not what I want...
In short, I'd want to load multiple items from the XML but with each being of their intended derived class so that they get access to their respective functions and get their specific properties such as BaseDamage if it's a weapon.
I've tried to use an XmlElement called ItemClass of type class, but I get an error saying that XmlElement/XmlAttribute is not valid on this declaration type...
I also thought about using an abstract Item class instead but then how do I load the item ID to the abstract base class Item and then BaseDamage to the derived class, Weapon?
This is the code I use to (deserialize? I'm not sure that's the correct term) the XML file :
[XmlRoot("ItemCollection")]
public class ItemContainer
{
[XmlArray("Items")]
[XmlArrayItem("Item")]
public List<Item> items = new List<Item>();
public static ItemContainer Load(string itemPath)
{
TextAsset _xml = Resources.Load<TextAsset>(itemPath);
XmlSerializer serializer = new XmlSerializer(typeof(ItemContainer));
StringReader reader = new StringReader(_xml.text);
ItemContainer items = serializer.Deserialize(reader) as ItemContainer;
reader.Close();
return items;
}
}
So, any help is welcome,
Thanks

Try this code example, a good tool for xml and c# is xml2csharp
class Program
{
static void Main(string[] args)
{
var xml = File.ReadAllText("test.xml");
var serializer = new XmlSerializer(typeof(ItemCollection));
using (var reader = new StringReader(xml))
{
var items = serializer.Deserialize(reader) as ItemCollection;
}
}
}
[XmlRoot(ElementName = "Item")]
public class Item
{
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "Damage")]
public string Damage { get; set; }
[XmlAttribute(AttributeName = "ID")]
public string ID { get; set; }
[XmlElement(ElementName = "Armor")]
public string Armor { get; set; }
}
[XmlRoot(ElementName = "Items")]
public class Items
{
[XmlElement(ElementName = "Item")]
public List<Item> Item { get; set; }
}
[XmlRoot(ElementName = "ItemCollection")]
public class ItemCollection
{
[XmlElement(ElementName = "Items")]
public Items Items { get; set; }
}

While the first answer wasn't exactly what I was looking for, it did send me on the correct track as I looked into using multiple roots and by googling for that, I fell on a certain website and realized that the solution was simply to tell the XML file that I'm not going to use the Item Class but rather Weapon / Armor / Scroll and such so that they can be considered as their respective derived class.
Did it like this :
[XmlRoot("ItemCollection")]
public class ItemContainer
{
[XmlArray("Items")]
[XmlArrayItem("Weapon", Type = typeof(Weapon))]
[XmlArrayItem("Armor", Type = typeof(Armor))]
public List<Item> items = new List<Item>();
public static ItemContainer LoadItems(string itemPath)
{
TextAsset _xml = Resources.Load<TextAsset>(itemPath);
XmlSerializer serializer = new XmlSerializer(typeof(ItemContainer));
StringReader reader = new StringReader(_xml.text);
ItemContainer items = serializer.Deserialize(reader) as ItemContainer;
reader.Close();
return items;
}
}
I haven't looked at protoBuff, the only thing I found was protocol buffer, which seems to be what you were talking about. I'll keep it in mind but right now it might be a bit too much for my basic understanding of C# in general
<?xml version="1.0" encoding="utf-8" ?>
<ItemCollection>
<Items>
<Weapon Name ="Dagger">
<ID>01</ID>
<BaseDamage>10</BaseDamage>
</Weapon>
<Armor Name ="Chainmail">
<ID>04</ID>
<BaseArmor>5</BaseArmor>
</Armor>
</Items>
</ItemCollection>

Related

xmlserialize argumentexception (ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List`1[System.String].)

Below piece of code is failing and throwing ArgumentException
static void Main(string[] args)
{
string xml = "<root><SourcePatient><Communication>HP:6055550120</Communication></SourcePatient></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var serializedString = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None,true);
var deserialise = serializedString.ToObject<SampleModel>();
}
Models are,
public class SampleModel
{
public SourcePatientModel SourcePatient { get; set; }
}
public class SourcePatientModel
{
public List<string> Communication { get; set; }
}
How to deserialize this? Sometimes Communication node from xml string will have multiple entries
Your current xml is only a single entry
<Communication>HP:6055550120</Communication>
Change your xml input
<Communication><Entry>HP:6055550120</Entry></Communication>
So later when you get multiple entries, they can be processed
<Communication><Entry>HP:6055550120</Entry><Entry>HP:xxxxxxxxx</Entry></Communication>
Your class needs tweaked a bit
if a string [] is acceptable
[XmlArray(ElementName = "Communication")]
[XmlArrayItem(ElementName = "Entry")]
public string[] comm_list // Whatever name you want here
{
get; set;
}
// if you want a list here
// also if your going to do this, realize it creates a new list every time you use it, not the best. (bad practice)
List<string> Communication
{
get => new List<string>(comm_list );
}
otherwise it gets a little complicated
[XmlRoot(ElementName="Communication")]
public class Communication // element name by def
{
[XmlElement(ElementName="Entry")]
public List<string> entry { get; set; }
}
Another possibility, not sure how your multiple entries come in.
If multiple entries look like the following
<Communication>HP:6055550120, HP:7055550120</Communication>
Then you cant have a direct list,
public class SourcePatientModel
{
public string Communication { get; set; }
// which again this creates a list everytime, its better to change your xml to match a tag name for each entry
[XmlIgnore]
public List<string> CommunicationValues { get => Communication.Split(',').ToList();
}
Also this is just typed up code, there may be some typos or compile errors
First I parsed xml to the valid model, then you can convert to json if needed
using (TextReader sr = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(SampleModel));
var schema = (SampleModel)serializer.Deserialize(sr);
}
[Serializable, XmlRoot("root")]
public class SampleModel
{
public SourcePatientModel SourcePatient { get; set; }
}
public class SourcePatientModel
{
[XmlElement("Communication")]
public List<string> Communication { get; set; }
}
I modified your classes to include some XML serialization attributes. (More on how to figure those out later - the short version is that you don't have to figure it out.)
[XmlRoot(ElementName = "SourcePatient")]
public class SourcePatientModel
{
[XmlElement(ElementName = "Communication")]
public List<string> Communication { get; set; }
}
[XmlRoot(ElementName = "root")]
public class SampleModel
{
[XmlElement(ElementName = "SourcePatient")]
public SourcePatientModel SourcePatientModel { get; set; }
}
...and this code deserializes it:
var serializer = new XmlSerializer(typeof(SampleModel));
using var stringReader = new StringReader(xmlString);
SampleModel deserialized = (SampleModel) serializer.Deserialize(stringReader);
Here's a unit test to make sure a test XML string is deserialized with a list of strings as expected. A unit test is a little easier to run and repeat than a console app. Using them makes writing code a lot easier.
[Test]
public void DeserializationTest()
{
string xmlString = #"
<root>
<SourcePatient>
<Communication>A</Communication>
<Communication>B</Communication>
</SourcePatient>
</root>";
var serializer = new XmlSerializer(typeof(SampleModel));
using var stringReader = new StringReader(xmlString);
SampleModel deserialized = (SampleModel) serializer.Deserialize(stringReader);
Assert.AreEqual(2, deserialized.SourcePatientModel.Communication.Count);
}
Here's a key takeaway: You don't need to memorize XML serialization attributes. I don't bother because I find them confusing. Instead, google "XML to Csharp" and you'll find sites like this one. I pasted your XML into that site and let it generate the classes for me. (Then I renamed them so that they matched your question.)
But be sure that you include enough sample data in your XML so that it can generate the classes for you. I made sure there were two Communication elements so it would create a List<string> in the generated class.
Sites like that might not work for extremely complicated XML, but they work for most scenarios, and it's much easier than figuring out how to write the classes ourselves.

XmlArray with multiple names

I've searched for hours and found similar results but not for this specific scenario. Consider the following XML file.
<root>
<largeImages>
<largeImage>
<url>./imageLarge.jpg</url>
<height>480</height>
<width>640</width>
</largeImage>
</largeImages>
<smallImages>
<smallImage>
<url>./imageSmall.jpg</url>
<height>240</height>
<width>320</width>
</smallImage>
</smallImages>
</root>
What I'm trying to do is deserialize this into a single array of images instead of 2 arrays.
public class root {
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public image[] largeImages { get; set; }
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
public image[] smallImages { get; set; }
}
This class gets me 2 arrays. root.largeImages and root.smallImages. Since my application doesn't care about large or small images I'd like to deserialize into the single array root.images. I've tried variations of XmlArray, XmlArrayItem, XmlElement and even XmlChoiceIdentifier without any success. I'm thinking something along the lines of the following which won't compile because apparently the XmlArrayAttribute can only be used once per property.
[XmlArray("largeImages")]
[XmlArray("smallImages")]
[XmlArrayItem("largeImage")]
[XmlArrayItem("smallImage")]
public image[] images { get; set; }
Obviously I could merge the 2 arrays in code after the XML is deserialized but it seems like this should be a simple thing to do.
XPATH is probably your answer assuming you don't really care about having it mapped to a class. XPath wildcards on node name gives an example of how you'd select multiple items - http://csharp.net-tutorials.com/xml/using-xpath-with-the-xmldocument-class/ gives an example of how it's used in C#.
Another way muight be using XSLT: Using the code: How to apply an XSLT Stylesheet in C# and XSLT like Combining elements from 2 lists in XSLT should get you what you want.
Personally I'd go with whatever makes life easiest since it doesn't look like you really care in this example what the intermediate data structure is.
Given that the answer from How to define multiple names for XmlElement field? works primarily for elements not arrays, the easiest solution would seem to be to introduce a surrogate property for one of the image elements, say <smallImages>:
public class root
{
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public List<image> Images { get; set; } = new List<image>();
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<image> SmallImagesSurrogate { get { return Images; } }
public bool ShouldSerializeSmallImagesSurrogate() { return false; }
}
Notes:
When deserializing to a pre-allocated List<T>, XmlSerializer will simply append deserialized elements to the existing list. That allows elements from both <largeImages> and <smallImages> to be concatenated into the same collection without data loss.
The surrogate property must be public but can be made "less visible" by setting attributes such as [Browsable(false)].
XmlSerializer can successfully deserialize get-only collection properties as long as the collection is pre-allocated. I took advantage of this to avoid adding a setter for the surrogate.
In case you need to re-serialize your root, the method ShouldSerializeSmallImagesSurrogate() will prevent the images array from being double-serialized. (For an explanation of why, see ShouldSerialize*() vs *Specified Conditional Serialization Pattern.) Instead all the images will serialize under <largeImages>. This method does not affect deserialization.
Sample working .Net fiddle.
Simple!!! I do it all the time. The trick is with root that you need to use Elements() and then use FirstOrDefault() to get only one.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Root root = doc.Elements("root").Select(x => new Root() {
Images = x.Descendants("largeImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList()
}).FirstOrDefault();
root.Images.AddRange(doc.Descendants("smallImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList());
}
}
public class Root
{
public List<Image> Images { get; set; }
}
public class Image
{
public string url { get; set; }
public int height { get; set; }
public int width { get; set; }
}
}
I had a challenge with model serialization, and thanks to MSDN I found the answer. Here is my solution:
public class Document
{
[XmlElement(ElementName = "seller_id")]
public string SellerId { get; set; }
[XmlArray(ElementName = "order_details")]
[XmlArrayItem(Type = typeof(SgtinCode), ElementName = "sgtin")]
[XmlArrayItem(Type = typeof(SsccCode), ElementName = "sscc")]
public Code[] Codes { get; set; }
}
public abstract class Code
{
[XmlText]
public string Value { get; set; }
}
public class SgtinCode : Code
{ }
public class SsccCode : Code
{ }
Model setup:
var document = new Document
{
SellerId = Guid.NewGuid().ToString(),
Codes = new Code[]
{
new SsccCode { Value = "111700126101510000000000011" },
new SsccCode { Value ="111700126101510000000000012" },
new SsccCode { Value ="111700126101510000000000013" },
new SgtinCode { Value = "abc" }
}
}
And XML output:
<?xml version="1.0" encoding="utf-8"?>
<documents>
<foreign_shipment>
<seller_id>fb2d35e7-c5d1-43ad-a272-89f897f41058</seller_id>
<order_details>
<sscc>111700126101510000000000011</sscc>
<sscc>111700126101510000000000012</sscc>
<sscc>111700126101510000000000013</sscc>
<sgtin>abc</sgtin>
</order_details>
</foreign_shipment>
</documents>

How to serialize a class with a property of type object filled with an array

After searching 99% of the net I am still stuck on the following matter. I have a web service which must comply to a wsdl that a partner company supplied. Calling a method of this service results in a (complex) class. Unfortunately a serialization error is raised when the service is called.
I have pinpointed the issue but cannot think of (and find) a solution to it. Because I'm dependant on the wsdl which was supplied, I cannot change the complex class structure. Hope anyone knows what I am missing. Here is example code to reproduce my issue:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
public object Item { get; set; } // <---- Note type *object* here
}
[System.SerializableAttribute()]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void Serializing()
{
MyObject myObject = new MyObject { Id = 1 };
myObject.Item = new MyItem[] { new MyItem { Id = 1, Name = "Test" } };
string serializedString = SerializeObjectToXmlString(myObject, new []{ typeof(MyItem)});
Assert.IsFalse(String.IsNullOrWhiteSpace(serializedString));
}
/// <summary>
/// This method serializes objects to an XML string using the XmlSerializer
/// </summary>
private static string SerializeObjectToXmlString(object theObject, Type[] types)
{
using (var oStream = new System.IO.MemoryStream())
{
var oSerializer = new System.Xml.Serialization.XmlSerializer(theObject.GetType(), types);
oSerializer.Serialize(oStream, theObject); // <- Here the error is raised
return System.Text.Encoding.Default.GetString(oStream.ToArray());
}
}
}
In the Try/Catch an error is raised after calling method Serialize(). Details of this error are:
InvalidOperationException was unhandled by user code
- There was an error generating the XML document.
The type MyItem[] may not be used in this context.
My development context consists of Visual Studio 2010, .Net Framework 3.5.
Edit #1: Added Serialization attributes but the error remaines
It appears that you cannot add an array of types to an object and serialize it. The solution was to create a container class which - like the name says - contains the array. This way you can assign the container class to the object and serialize it all.
In addition to my case, I was mislead by the object model created by the wsdl.exe utility, since the container class is only a technical solution to add an array to an object. This container class was also created so everything was already there to use. Only after trying out my custom container class I noticed the already created container class. Lost a lot of time on this matter unfortunately...
You should mark you classes as
[Serializable]
public class MyObject
{
public int Id { get; set; }
public MyItem[] Item { get; set; } // <---- Note type *object* here
}
[Serializable]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Serialize uknown object (Item of MyObject class) you will need to do manually by implementing proper interfaces:
ISerializable and IDeserializationCallback, botha added to MyObject class.
This is an old question, but I had the same problem and found a different solution, so I thought I'd share in case it helps someone else.
I found that I could add attributes to allow arrays of specific types. For the problem above, the MyObject class could be edited as below:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
[XmlElement(Type = typeof(object), ElementName = "Item"), //added
XmlElement(Type = typeof(MyItem[]), ElementName = "Item_asArrayOfMyItem")] //added
public object Item { get; set; } // <---- Note type *object* here
}
Anything that serialized before will still look the same, but now MyObject can be serialized even when Item has type MyItem[], as in the question's test case.

Serializing class, "move" attribute to other element

I'm trying to constrol where a class property is rendered when the class is serialized: I need the property to appear as an attribute on a specific element:
namespace ConsoleApplication6
{
public class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.MyList.Add(new Item() { ID = 1 });
myClass.MyList.Add(new Item() { ID = 2 });
myClass.Xxx = "Hello World!";
var sx = new XmlSerializer(myClass.GetType());
sx.Serialize(Console.Out, myClass);
}
public class MyClass
{
public MyClass()
{
MyList = new List<Item>();
}
public List<Item> MyList { get; set; }
[XmlAttributeAttribute(AttributeName = "x")]
public string Xxx { get; set; }
}
public class Item
{
public int ID { get; set; }
}
}
}
This serializes quite nicely into this:
<?xml version="1.0" encoding="ibm850"?>
<MyClass xmlns:xsi=" ... " xmlns:xsd=" ... " x="Hello World!">
<MyList>
<Item>
<ID>1</ID>
</Item>
<Item>
<ID>2</ID>
</Item>
</MyList>
</MyClass>
BUT: My problem is, I need the property Xxx to be rendered as an attribute on the <MyList> element rather than the <MyClass> root element, like this:
...
<MyList x="Hello World!">
...
I'm GUESSING this should be possible using XmlSerialization attributes on the class/properties, but I can't figure it out. I even tried creating a subclass of List adding the property Xxx to that, but the .NET XML serialization ignores the extra properties, and the XML output is just like the List<..> is normally serialized.
Update: Here's the code where I try to create a "custom list", that inherits from List<Item> and adds an extra property:
public class Program
{
static void Main(string[] args)
{
var myClass = new MyClass();
myClass.MyList.Add(new Item() { ID = 1 });
myClass.MyList.Add(new Item() { ID = 2 });
myClass.MyList.Xxx = "Hello World!";
var sx = new XmlSerializer(myClass.GetType());
sx.Serialize(Console.Out, myClass);
}
public class MyClass
{
public MyClass()
{
MyList = new CustomList();
}
public CustomList MyList { get; set; }
}
public class Item
{
public int ID { get; set; }
}
public class CustomList : List<Item>
{
[XmlAttributeAttribute(AttributeName = "x")]
public string Xxx { get; set; }
}
}
The output xml looks like this:
<?xml version="1.0" encoding="ibm850"?>
<MyClass xmlns:xsi=" ... " xmlns:xsd=" ... ">
<MyList>
<Item>
<ID>1</ID>
</Item>
<Item>
<ID>2</ID>
</Item>
</MyList>
</MyClass>
Notice how the Xxx property is not represented in the xml...
I think for that level of control you will need to look at using the IXmlSerializable interface. I don't think using attributes will work here.
According to this MSDN discussion:
XmlSerializer does not serialize any members if a collection. Only
collection items get serialized. This is by design, basically a
decision was made to handle collections as arrays not as classes with
multiple properties, so collections should look like arrays on the
wire, therefore they do not have any members other then collection
items, and can be “flattened” by adding the [XmlElement] to the member
of the ICollection type.
So apparently the flaw you describe is by design. Unless you decide to run the resulting XML through some transforms, I'm not sure how you plan to get this to work using the XML serialization attributes.
This post provides some additional information, including a few options:
When a class is inherited from List<>, XmlSerializer doesn't serialize other attributes
Summary:
IXmlSerializable (as mentioned by Kai)
DataContractSerializer
Create a new class where X (your attribute) is a property and provide an additional property that is a list (so instead of subclassing list, just create a wrapper class). For example:
public class MyListWrapper<T>
{
public MyListWrapper()
{
Data = new List<T>();
}
[XmlAttribute(AttributeName="x")]
public string Xxx { get; set; }
[XmlElement]
public List<T> Data { get; set; }
}
Note that this will output Items as "Data" elements. If you're willing to drop the generic type parameter on Data and make it List(Item) then you can get items.
Hope that helps!

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

Categories