XmlSerializer namespaces

Xml is a data-language - it allows you to describe and carry data in one file, which is awesome for inter-system communication. Another nice feature is the ability to version your data. This is done using namespaces.

<myRoot xmlns="http://porse.prg/data/2019/01/11">
    <myElement />
    <yourElement />
    <herElement />

See, all the data belongs to the namespace http://porse.org/data/2019/01/11 so I know what to expect - especially if I also produce a schema definition.

So in a month's time or so, I'll probably release v2 which introduces the field <ourElement />. the other elements are unaltered, and so I could do something like the following

<myRoot xmlns="http://porse.org/data/2019/02/03"  xmlns:v1="http://porse.prg/data/2019/01/11">
    <v1:myElement />
    <v1:yourElement />
    <v1:herElement />
    <ourElement />

Notice that the "default" namespace changed to /2019/02/03, v1 was explicitly named and I reused the elements my-/your-/her from v1 in my "new" myRoot type. And here's the point of my post: It's not obvious how make System.Xml.Serialization.XmlSerializer produce the output from above.

Let's assume we write these classes and have XmlSerializer serialize it


[XmlType(Namespace = "http://porse.org/data/2019/01/11")]
public class Data { ... }

[XmlType(Namespace = "http://porse.org/data/2019/02/03")]
public class Data2 { ... }

[XmlRoot("myRoot", Namespace="http://porse.org/data/2019/02/03")]
public class myRoot 
    public Data My {get; }

    public Data Your {get;}

    public Data Her {get;}

    public Data2 Our {get;}

XmlSerializer ser = new XmlSerializer(typeof(myRoot));
ser.Serialize(outStream, instanceOfMyRoot);

The result would be something like this:

<myRoot xmlns="http://porse.org/data/2019/02/03">
    <myElement xmlns="http://porse.org/data/2019/01/11" />
    <yourElement xmlns="http://porse.org/data/2019/01/11" />
    <herElement xmlns="http://porse.org/data/2019/01/11" />
    <ourElement />

Which technically is correct, but it's hard on the eyes and the bandwidth. To make it behave you need to tell the serializer, that you have more namespaces in the mix. This is done using the AttributesOverride parameter in the XmlSerializer constructor. But this is for your ad-hoc serialization needs, if for instance you are calling web services using svcutil-generated classes, they will use the default serializer constructor without AttributeOverrides.

But you can declare the overrides in code:

[XmlRoot("myRoot", Namespace="http://porse.org/data/2019/02/03")]
public class MyRoot
    public XmlSerializerNamespaces MyCustomNamespaces;

    public MyRoot()
        MyCustomNamespaces = new XmlSerializerNamespace();
        MyCustomNamespaces.Add("v1", "http://porse.org/data/2019/01/11");
    ... etc

When the serializer happens upon a MyRoot instance, it knows to look for a field or property with the XmlNamespaceDeclarationsAttribute and insert these. The field MyCustomNamespaces will not be serialized to the output, and we'll save 114 bytes in the transmission and gain a lot of readability.





Comments are closed