JOSEFtw
JOSEFtw

Reputation: 10091

Deserialize dynamic XML with generics C#

Given the following XML:

<RESPONSE version="10">
    <RESULTS MyAttribute="1" />
</RESPONSE>

I can deserialize it like this:

[XmlRoot("RESPONSE")]
public class Response
{
    [XmlElement(ElementName = "RESULTS")]
    public Results Results {get;set;}
}

public class Results
{
    [XmlAttribute(AttributeName = "MyAttribute")]
    public bool MyAttribute { get; set; }
}

var serializer = new XmlSerializer(typeof(Response));
var result = (Response)serializer.Deserialize(new StringReader(xml));

This works and is fine. The problem however is that I want to deserialize the following XML as well and I don't want to duplicate the Response class, I want to use a generic approach.

<RESPONSE version="10">
    <SOCCER AnotherAttribute="1" />
</RESPONSE>

I've tried doing the following:

[XmlRoot("Response")]
public class Response<T>
{
    public T Data {get;set;}
}

public class SoccerRoot
{
    [XmlElement(ElementName = "SOCCER")]
    public class Soccer {get;set;}
}

public class Soccer
{
    [XmlAttribute(AttributeName = "AnotherAttribute")]
    public bool AnotherAttribute {get;set;}
}


var serializer = new XmlSerializer(typeof(Response<SoccerRoot>));
var result = (Response<SoccerRoot>)serializer.Deserialize(new StringReader(xml));

But this doesn't work, I've also tried to inherit the Response class but with no luck. Im getting the following error: <xmlns='' > was not expected.

Am I on the right way here or is it not possible to use a generic approach when deserializing XML files?

Upvotes: 4

Views: 424

Answers (2)

Rudresha Parameshappa
Rudresha Parameshappa

Reputation: 3926

Try to create a common class which contains both the objects RESULTS and SOCCER and then based on the data one will be null and other will contain value.

class Program
{
    static void Main(string[] args)
    {
        //
        var xml = "<RESPONSE  version=\"1\" ><SOCCER AnotherAttribute =\"1\" /></RESPONSE>";
        var serializer = new XmlSerializer(typeof(Response<int>));
        var result = (Response<int>)serializer.Deserialize(new StringReader(xml));

        xml = "<RESPONSE version=\"10\"><RESULTS MyAttribute = \"1\" /></RESPONSE >";
        result = (Response<int>)serializer.Deserialize(new StringReader(xml));
        Console.ReadLine();
    }
}

[XmlRoot("RESPONSE")]
public class Response<T>
{
    [XmlAttribute(AttributeName = "version")]
    public T Version { get; set; }

    [XmlElement(ElementName = "SOCCER")]
    public SoccerRoot SoccerRoot { get; set; }

    [XmlElement(ElementName = "RESULTS")]
    public Results Results { get; set; }
}
public class SoccerRoot
{
    [XmlAttribute(AttributeName = "AnotherAttribute")]
    public int AnotherAttribute { get; set; }
}

public class Results
{
    [XmlAttribute(AttributeName = "MyAttribute")]
    public bool MyAttribute { get; set; }
}

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063338

With Response<SoccerRoot>, the expected xml layout would be:

<RESPONSE>
    <Data>
        <SOCCER AnotherAttribute=...>

which does not match what you have. There is no way of customizing the element name of the T Data just using attributes.

Options:

  • have multiple root objects (one each for SOCCER, RESULTS, etc):

    [XmlRoot("RESPONSE")]
    public class SoccerRoot {
        [XmlElement("SOCCER")]
        public Soccer Soccer {get;set;}
    }
    [XmlRoot("RESPONSE")]
    public class ResultsRoot {
        [XmlElement("RESULTS")]
        public Results Results {get;set;}
    }
    
  • have a single root object that has multiple first-level children (one for SOCCER, one for RESULTS, etc - each of one type

    [XmlRoot("RESPONSE")]
    public class Response {
        [XmlElement("RESULTS")]
        public Results Results {get;set;}
        [XmlElement("SOCCER")]
        public Soccer Soccer {get;set;}
    }
    
  • use the generics approach, but lose the intermediate object so you have Response<Soccer> (delete SoccerRoot completely), and use XmlAttributeOverrides to customize the element name, being sure to cache the XmlSerializer generated (otherwise it will leak at an alarming rate)

Frankly, I'd go with either of the first two options and leave the third one alone.

Upvotes: 3

Related Questions