Imagine having XML structure like the following:
[XmlRoot("Foo")]
public class Foo
{
[XmlElement("Bar")]
public Bar Bar { get; set; }
[XmlElement("SuperImportant")]
public SuperImportant SuperImportant { get; set; }
}
[XmlRoot("Bar")]
public class Bar
{
[XmlElement("Baz")]
public XmlElement Baz { get; set; }
}
[XmlRoot("SuperImportant")]
public class SuperImportant
{
[XmlElement("MegaImportant")]
public string MegaImportant { get; set; }
}
Baz defined as XmlElement
for some reason.
Now check this code:
var template = @"
<Foo>
<Bar>
{0}
</Bar>
<SuperImportant>
<MegaImportant>42</MegaImportant>
</SuperImportant>
</Foo>";
var selfClosed = new StringReader(String.Format(template, "<Baz/>"));
var openClosePair = new StringReader(String.Format(template, "<Baz></Baz>"));
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Foo));
var o1 = (Foo)xmlSerializer.Deserialize(selfClosed);
Console.WriteLine(o1.SuperImportant == null); // True, it's not there
var o2 = (Foo)xmlSerializer.Deserialize(openClosePair);
Console.WriteLine(o2.SuperImportant == null); // False, it's there
As you can see, if some tag, defined as XmlElement
in class definition appears to be self closed, the element right after its parent tag is null
ed. How can I configure XmlSerializer
to treat self-closed tags as open-close pairs? SuperImportant
should be deserialized in both cases, but it's not in the former, which is wrong.
You should mark the property Baz
with [XmlAnyElement("Baz")]
like so:
[XmlRoot("Bar")]
public class Bar
{
[XmlAnyElement("Baz")]
public XmlElement Baz { get; set; }
}
As explained in the docs, this attribute
Specifies that the member (a field that returns an array of XmlElement or XmlNode objects) contains objects that represent any XML element that has no corresponding member in the object being serialized or deserialized.
...
You can also apply the XmlAnyElementAttribute to a field that returns a single XmlElement object. If you do so, you must use the properties and methods of the XmlElement class to recursively iterate through the unknown elements.
Once the attribute is applied, the Baz
property will correctly capture both the <Baz/>
and <Baz></Baz>
elements without interfering with deserialization of subsequent node(s). It does seem odd that [XmlElement("Baz")]
causes the inconsistency you are seeing, but since [XmlAnyElement("Baz")]
is designed to handle this situation it should be used instead.
Sample working .Net fiddle here.