I wonder how to implement the method ReadXml of the IXmlSerializable interface when my XML contains recursive tags like in the following example:
<?xml version='1.0' encoding='utf-8'?>
<dform>
<label name='label-a' text='A' dbpath='module/label-a'/>
<textmemo name='textmemo-a' text='' dbpath='module/textmemo-a'/>
<section name='section-a' text='' dbpath='module/section-a'>
<textmemo name='textmemo-b' text='' dbpath='module/textmemo-b'/>
</section>
<section name='section-b' text='' dbpath='module/section-b'>
<textmemo name='textmemo-c' text='' dbpath='module/textmemo-c'/>
<label name='label-c' text='A' dbpath='module/label-c'/>
<section name='section-c' text='' dbpath='module/section-c'>
<label name='label-d' text='A' dbpath='module/label-d'/>
</section>
</section>
</dform>
The element <section>
works like a container for all kind of elements, itself included.
I need to translate this structure into objects: a List of elements (objects) that contains other List objects when the element is a Section. So I've created the following Interface and Classes:
public interface IWidget
{
String type { get;}
String name { get; set; }
String labelCation { get; set; }
String text { get; set; }
String dbpath { get; set; }
}
class Textmemo : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Textmemo()
{
type = "CONTROL";
}
}
class Label : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Label()
{
type = "LABEL";
}
}
class Section : IWidget
{
public String type { get; }
public String name { get; set; }
public String labelCation { get; set; }
public String text { get; set; }
public String dbpath { get; set; }
public List<IWidget> subsection { get; set; }
public Section()
{
type = "SECTION";
subsection = new List<IWidget>();
}
}
The Section class has one more property compared with the interface IWidget from which it inherits, that is the subsection property.
Then I was ready to move my steps starting from this example XmlSerializer serialize generic List of interface
but I really don't understand how to manage the <section>
element in my code, that, at the moment, it is nothing more than the signature of the class:
public class WidgetsList: List<IWidget>, IXmlSerializable
{
public WidgetsList() : base() { }
public System.Xml.Schema.XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
}
public void WriteXml(XmlWriter writer)
{
}
}
Thanks a lot for the advice!
Why are you wanting to implement IXmlSerializable
yourself? You can use various attributes to have the framework do this for you. For example, define a base class (given all your implementations are the same):
public abstract class Widget
{
[XmlIgnore]
public abstract string type { get;}
[XmlAttribute]
public string name { get; set; }
[XmlIgnore]
public string labelCation { get; set; }
[XmlAttribute]
public string text { get; set; }
[XmlAttribute]
public string dbpath { get; set; }
}
The attributes here specify that type
and labelCation
are ignored (as they don't exist in the XML). The rest are mapped to XML attributes (e.g. name='abc'
).
You can then create your three sub-types:
public class Textmemo : Widget
{
public override string type { get; } = "CONTROL";
}
public class Label : Widget
{
public override string type { get; } = "LABEL";
}
public class Section : Widget
{
public override string type { get; } = "SECTION";
[XmlElement("textmemo", Type=typeof(Textmemo))]
[XmlElement("label", Type=typeof(Label))]
[XmlElement("section", Type=typeof(Section))]
public List<Widget> subsection { get; } = new List<Widget>();
}
Section
is the interesting one as it defines the types permitted as child elements with the relevant name mappings. Similarly to this, you'd then define the root element:
[XmlRoot("dform")]
public class DForm
{
[XmlElement("textmemo", Type=typeof(Textmemo))]
[XmlElement("label", Type=typeof(Label))]
[XmlElement("section", Type=typeof(Section))]
public List<Widget> Widgets { get; } = new List<Widget>();
}
You can see in this demo that your XML round-tripped through the serializer is the same, thus proving this works.