Explicit data contracts that allow for arbitrary, unknown elements are not supported by DataContractSerializer
. XmlSerializer
supports this via [XmlAnyElementAttribute]
, but as stated in the answer Using [XmlAnyElement], there is no identical functionality for data contracts.
Your class could implement IExtensibleDataObject
. It is similar to [XmlAnyElement]
and is intended for forward-compatible data contracts. Unfortunately, in that case the unknown elements are stored in an opaque ExtensionDataObject
with no obvious way to access the values. While it is possible to extract the XML from such an object (see here) it's nonobvious and is unlikely to be more performant than your current code, as it requires re-serializing the ExtensionDataObject
inside a wrapper class, then parsing the result.
One note about performance - when you do XDocument.Parse(reader.ReadOuterXml())
, the reference source shows you are effectively parsing your XML, then streaming it through an XmlWriter
to a StringWriter
, then parsing the resulting string a second time. Rather than doing this, you can parse the XML only once by calling XNode.ReadFrom()
on the incoming reader, like so:
public class CustomObject : IXmlSerializable
{
private readonly Dictionary<String, String> attributes = new Dictionary<string, string>();
public IDictionary<string, string> Attributes { get { return attributes; } }
#region IXmlSerializable Members
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
var element = XElement.ReadFrom(reader) as XElement;
if (element != null)
{
foreach (var item in element.Elements())
attributes.Add(item.Name.LocalName, (string)item);
}
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
// Do NOT write the wrapper element when writing.
foreach (var pair in attributes)
{
writer.WriteElementString(pair.Key, pair.Value);
}
}
#endregion
}
This should be more performant than your current class. For instance, in Web API performance issues with large dynamic XML the reported improvement for a similar optimization was 40%.
Update
For the best possible performance implementing IXmlSerializable
you will need to read content directly from the XmlReader
using bespoke code. The following, for instance, reads element names and values into the attributes
dictionary:
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.Read();
while (reader.NodeType != XmlNodeType.EndElement)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
var key = reader.Name;
var value = reader.ReadElementContentAsString();
attributes.Add(key, value);
break;
default:
// Comment, for instance.
reader.Read();
break;
}
}
// Consume the EndElement
reader.Read();
}
See Proper way to implement IXmlSerializable? for some general guidelines on manually reading an element hierarchy correctly.