views:

213

answers:

1

When using Xml serialization in C#, I want to deserialize a part of my input XML to an XmlNode.

So, given this XML:

<Thing Name="George">
  <Document>
    <subnode1/>
    <subnode2/>
  </Document>
</Thing>

I want to deserialize the Document element to an XmlNode.

Below is my attempt which given the XML above, sets Document to the 'subnode1' element rather than the 'Document' element.

How would I get the code to set the Document property to the Document element?

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

[Serializable]
public class Thing
{
  [XmlAttribute] public string Name {get;set;}
  public XmlNode Document { get; set; }
}

class Program
{
  static void Main()
  {
    const string xml = @"
<Thing Name=""George"">
  <Document>
    <subnode1/>
    <subnode2/>
  </Document>
</Thing>";
    var s = new XmlSerializer(typeof(Thing));
    var thing = s.Deserialize(new StringReader(xml)) as Thing;
  }
}

However, when I use an XmlSerializer to deserialize the XML above to an instance of Thing, the Document property contains the child element 'subnode1', rather than the 'Document' element.

How can I get the XmlSerializer to set Document to an XmlNode containing the 'Document' element?

(NB: I can get to the Document element by specifying a property of type XmlElement[] and marking it [XmlAnyElement], but that is set to the array of all unrecognised elements rather than just a single one named 'Document')

+2  A: 

Try marking the Document property with the [XmlAnyElement] attribute.

[Serializable]
public class Thing
{
    [XmlAttribute] 
    public string Name {get;set;}

    [XmlAnyElement]
    public XmlNode Document { get; set; }
}

This will have one unfortunate side effect in that you will find more than just subnode1 and subnode2 (all whitespaces get deserialized too... ugh), so you will need to filter them out:

class Program
{
    static void Main()
    {
        const string xml = @"
<Thing Name=""George"">
  <Document>
    <subnode1/>
    <subnode2/>
  </Document>
</Thing>";
        var s = new XmlSerializer(typeof(Thing));
        var thing = s.Deserialize(new StringReader(xml)) as Thing; 

        foreach (XmlNode node in thing.Document)
        {
            // should filter to only subnode1 and subnode2.
            if (node.Name != "" && node.Name != "#whitespace")
            {
              Console.WriteLine(node.Name);
            }
        }

        Console.ReadLine();
    }
}

Hope this helps!

code4life
At the bottom of my question I mentioned that a workaround was to use the XmlAnyElement attribute, but I didnt know it could be placed on a single XmlNode. Maybe you answered before I added that text. I prefer capturing all the unrecognised elements though, so I can detect errors in the XML (ie there should only be one child element).
mackenir