Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have XML that looks something like this:

<root>
    <base type="a">
        <common>1</common>
        <concreteA>one</concreteA>
    </base>
    <base type="b">
        <common>2</common>
        <concreteB>two</concreteB>
    </base>
</root>

And classes like this:

public class Root
{
    public List<Base> Bases { get; set; }
}

public class Base
{
    public int Common { get; set; }
}

public class A : Base
{
    public string ConcreteA { get; set; }
}

public class B : Base
{
    public string ConcreteB { get; set; }
}

How can I deserialize this into objects? I've seen many posts on how to do it when each base node has a different name using XmlArrayItemAttribute( ElementName, Type )], but I need to choose it based on the elements type attribute instead.

share|improve this question
    
Do you want the type attribute to be the type of the class? I.e. the first base element will deserializa into a class A? –  Ned Stoyanov Jun 7 '13 at 3:57
    
Yes! I'll add class examples to the question to make it more apparent. –  Josh Close Jun 7 '13 at 4:04
    
I think you'll need to implement IXmlSerializable and then manually parse out the hierarchy int the ReadXml(XmlReader reader) method –  Ned Stoyanov Jun 7 '13 at 4:54
add comment

1 Answer

up vote 0 down vote accepted

This is a very basic way of doing it, but I think it works. I think if the class type was an element instead of an attribute then you should be able to parse it declaratively.

The code basically switches on the type attribute top determine what subclass to create and then manually populates the concrete properties and the common property.

[System.Xml.Serialization.XmlType("base")]
public class Base
{
    [System.Xml.Serialization.XmlElement("common")]
    public int Common { get; set; }
}

public class A : Base
{
    public string ConcreteA { get; set; }
}

public class B : Base
{
    public string ConcreteB { get; set; }
}

[System.Xml.Serialization.XmlRootAttribute("root")]
public class Root : System.Xml.Serialization.IXmlSerializable
{
    [System.Xml.Serialization.XmlElement("base")]
    public List<Base> Bases { get; set; }

     public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        this.Bases = new List<Base>();
        var document = XDocument.Load(reader);

        foreach (var element in document.Root.Elements())
        {
            Base baseElement = null;

            var attr = element.Attribute("type");

            if(attr.Value == "a")
            {
                var a = new A();
                a.ConcreteA = element.Element("concreteA").Value;
                baseElement = a;
            }
            else
            {
                var b = new B();
                b.ConcreteB = element.Element("concreteB").Value;
                baseElement = b;
            }

            baseElement.Common = int.Parse(element.Element("common").Value);
            this.Bases.Add(baseElement);
        }

        this.Bases.Dump();
    }

    public void WriteXml(XmlWriter writer)
    {
        throw new NotSupportedException();
    }
}

void Main()
{
    var xmlString = @"<root>
    <base type=""a"">
        <common>1</common>
        <concreteA>one</concreteA>
    </base>
    <base type=""b"">
        <common>2</common>
        <concreteB>two</concreteB>
    </base>
</root>";

    var stream = new StringReader(xmlString);
    var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(Root));
    var result = (Root)deserializer.Deserialize(stream);    
}
share|improve this answer
    
Basic works. ;) How would this work with sub objects on one of those classes? Is there an automatic way of doing those, or does everything in the graph have to be manual this way? –  Josh Close Jun 7 '13 at 6:41
    
I think they will need to implement System.Xml.Serialization.IXmlSerializable and when you read an element of the subobject class name you can create the subobject instance and call ReadXml on it. See this post –  Ned Stoyanov Jun 7 '13 at 11:34
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.