To expound on Heinzi's answer, I needed to change the default namespace (technically, the namespace of the root element) so I could deserialize documents using XmlAttributeOverrides
applied to a class hierarchy I didn't control. As part of that, I had to assign an XmlRootAttribute
attribute to the first class. The problem is that XmlSerializer
expects the Namespace value to match the XmlRootAttribute
's namespace in order to deserialize a document, which cannot be guaranteed.
With the following class, derived from XmlReader
, the root element's namespace can be assigned a known value for the deserializer. The reader's namespace can be forced to match the XmlRootAttribute
attribute's namespace (even if it is an empty string).
Simplifying the solution is using the XmlWrappingReader
from Alterant's answer on StackOverflow to How do I create a XmlTextReader that ignores Namespaces and does not check characters.
/// <summary>
/// XML document reader replaces the namespace of the root element.
/// </summary>
public class MyXmlReader : Mvp.Xml.Common.XmlWrappingReader
{
// Namespace of the document's root element. Read from document.
private string rootNamespace = "";
/// <summary>
/// Get or set the target namespace to use when deserializing.
/// </summary>
public string TargetNamespace { get; set; }
/// <summary>
/// Initialize a new instance of the MXmlReader class.
/// </summary>
/// <param name="reader">XmlReader instance to modify.</param>
public MyXmlReader(XmlReader reader) : base(reader)
{
TargetNamespace = "";
}
/// <summary>
/// Return the namespace of the XML node. Substitute the target namespace if it matches the namespace of the root element.
/// </summary>
public override string NamespaceURI
{
get
{
if (Depth == 0 && NodeType == XmlNodeType.Element)
{
// Save the namespace from the document's root element.
rootNamespace = base.NamespaceURI;
}
if (base.NamespaceURI == rootNamespace)
{
// Substitute the schema's targetNamespace for the root namespace.
return TargetNamespace;
}
// Use the native namespace of the XML node.
return base.NamespaceURI;
}
}
}
I instantiated MyXmlReader and used it do deserialize to an object marked with XmlRootAttribute(ElementName = "DocumentRoot", Namespace = "http://my.target.namespace")
:
var reader = new MyXmlReader(XmlReader.Create(stream));
reader.TargetNamespace = "http://my.target.namespace";
// Deserialize using the defined XML attribute overrides that can
// supply XML serialization attributes to types at runtime.
Type t = typeof(SomeDeserializedObject);
var xo = SomeDeserializedObject.GetXmlAttributeOverrides();
XmlSerializer serializer = new XmlSerializer(t, xo);
SomeDeserializedObject o = (SomeDeserializedObject)serializer.Deserialize(reader);
In the event the XML document I import has a different root namespace, or doesn't specify one at all, now I can still deserialize it.