views:

448

answers:

2

I have some XML that I deserialize into a business object. I am using XmlSerializer.Deserialize to do so. However, I want one of the XmlElement contained in the XML to stay an XElement.

It cannot be done directly (using an XmlElementAttribute) since XElement is not Serializable. I also tried to serialize that element to a string (in a two steps attempt to get an XElement), but that failed with the error:

unexpected node type element. readelementstring method can only be called on elements with simple or empty content

Any idea how that can be done?

Here is an example of xml and the resulting object I want:

<Person name="Joe">
  <Hobbies>
    <Hobby name="Reading" .../>
    <Hobby name="Photography" .../>
  </Hobbies>
  <HomeAddress>
    ...
  </HomeAddress>
</Person>

Object:

 public class Person
    {
      [XmlAttribute("Name")]
      public string Name {get; set;}
      ?????
      public XElement Hobbies {get; set;}
      [XmlElement("HomeAddress")]
      public Address HomeAddress {get; set;}
    }

The attempts that do not work:

[XmlElement("Hobbies")]
public XElement Hobbies {get; set;}
[XmlElement("Hobbies")]
public string Hobbies {get; set;}
+1  A: 

To have full control (along with full responsibility) for how the XML is generated you can have your class implement the System.Xml.Serialization.IXmlSerializable interface, and override ReadXml and WriteXml. I've had to do this before with dictionary classes - be sure to test thoroughly, especially with null properties, empty fields, etc.

http://www.devx.com/dotnet/Article/29720

sdcoder
+1  A: 

To avoid the hard work of implementing something like IXmlSerializable, you might do something along the lines of a semi-hidden pass-thru XmlElement property; note, however, that this doesn't quite do what you want since you can only have one root XElement value (not two, as per your example); you would need a list to do that...

using System;
using System.ComponentModel;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
public class Person
{
    [XmlAttribute("Name")]
    public string Name { get; set; }
    [XmlIgnore]
    public XElement Hobbies { get; set; }

    [XmlElement("Hobbies")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public XmlElement HobbiesSerialized
    {
        get
        {
            XElement hobbies = Hobbies;
            if(hobbies == null) return null;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(hobbies.ToString());
            return doc.DocumentElement;
        }
        set
        {
            Hobbies = value == null ? null
                : XElement.Parse(value.OuterXml);
        }
    }
    [XmlElement("HomeAddress")]
    public Address HomeAddress { get; set; }
}

public class Address { }

static class Progmam
{
    static void Main()
    {
        var p = new Person { Hobbies = new XElement("xml", new XAttribute("hi","there")) };
        var ser = new XmlSerializer(p.GetType());
        ser.Serialize(Console.Out, p);
    }
}
Marc Gravell
Very nice. I don't understand why it takes the inner node when specifying the XmlElementAttribute to 'Hobbies'. I however have control ove the XML, so I simply added a wrapper node around the 'Hobbies' node to get what I needed (instead of using a list). Thanks a lot.
joerage