views:

82

answers:

2

I'm trying to write a generic method that can be used to deserialize xml to an array of objects.

Given XML that looks like so:

<people>
    <person>
        <someElement>content</someElement>
    </person>
    <person>
        <someElement>more content</someElement>
    </person>
</people>

Shown in the below code as xmlDoc. And a person class as T

XmlNodeReader reader = new XmlNodeReader(xmlDoc.DocumentElement);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T[]), new XmlRootAttribute(xmlDoc.DocumentElement.Name));
results = xmlSerializer.Deserialize(reader) as T[];

This works as expected, and returns person[] with 2 entries.

However, in the API I am working with, if there is only 1 result returned, it just returns:

<person>
    <someElement>content</someElement>
</person>

And my deserialization breaks down. person[] remains empty.

Any thoughts on the best way to implement this?

Edit

I'm contemplating running an XSLT in between, and passing the name of T in, if it matches the root node, then add a wrapping node?

A: 

Check Name of the Root element, and if it is not people, add it to xml, and everything will go fine.


Update: Check deep of the xml document, and if its == 2, create root element. Another way - use of LINQ-TO-XML XElement.Descandants("person") - array of person-elements

VMAtm
Need a way to handle this generically. The posted sample is just an example, there is >100 different xml types.
DaRKoN_
Updated the answer
VMAtm
Can't check for depth, as the real data could be a number of depths, also checking for a specific node using L2Xml wouldn't be generic.
DaRKoN_
A: 

I ended up using XSLT to ensure the node(s) I was after weren't the root.

Basically I've a XSLT file containing:

<xsl:template match="/">
    <rootNode>
        <xsl:apply-templates select="node()|@*"/>
    </rootNode>
</xsl:template>

(Not sure if this XSLT is ideal, would love some comments).

This wraps a around my inbound XML from the api. My previously mentioned Person class has a [XmlType("person")] attribute applied to it, armed with that I can do:

//using reflection to look what the XmlType has been declared on this type
var typeAttributes = Attribute.GetCustomAttribute(typeof(T), typeof(XmlTypeAttribute));

//determine an xpath query to find this type of elements parent
string xPathtoTypeName = string.Format("//{0}/parent::node()", ((XmlTypeAttribute)typeAttributes).TypeName);

//use the above xpath query to find the parent node.
var parentNode = transformedDocument.SelectSingleNode(xPathtoTypeName);

//deserialize as before
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T[]), new XmlRootAttribute(parentNode.Name));
XmlNodeReader nodeReader = new XmlNodeReader(parentNode);
results = xmlSerializer.Deserialize(nodeReader) as T[];
DaRKoN_