Search code examples
c#xml-serializationxml-namespacesdatacontractserializer

Predefine XML namespaces for DataContractSerializer


I'm building a self hosted WCF service. I'm building a special data structure for a very flexible transport of data. So far I test if my structure is serializable using the DataContractSerializer. That works fine and I'm happy about that, but there is something annoying me:

In my XML output are dozens redefined xmlns attributes e.g.:

xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
xmlns:b="http://www.w3.org/2001/XMLSchema"

This should be better defined once in the root element so that bytes could be simply optimized. Is there a way to add custom namespace informations to the root element?

Here is a bigger example to demonstrate what I mean:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test"
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Data xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <a:KeyValueOfstringanyType>
      <a:Key>ID</a:Key>
      <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:Value>
    </a:KeyValueOfstringanyType>
    <a:KeyValueOfstringanyType>
      <a:Key>Value</a:Key>
      <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">42</a:Value>
    </a:KeyValueOfstringanyType>
  </Data>
  <Data xmlns:a="...">...</Data>
  <Data xmlns:a="...">...</Data>
  <Data xmlns:a="...">...</Data>
</DataObject>

What I want is something like this:

<DataObject xmlns="http://schemas.datacontract.org/2004/07/Test"
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
            xmlns:b="http://www.w3.org/2001/XMLSchema">
  <Data>
    <a:KeyValueOfstringanyType>
      <a:Key>ID</a:Key>
      <a:Value i:type="b:int">1</a:Value>
    </a:KeyValueOfstringanyType>
    <a:KeyValueOfstringanyType>
      <a:Key>Value</a:Key>
      <a:Value i:type="b:int">42</a:Value>
    </a:KeyValueOfstringanyType>
  </Data>
  <Data>...</Data>
  <Data>...</Data>
  <Data>...</Data>
</DataObject>

Solution

  • static void Main()
    {
        var o = new Foo {
            Prop = new Dictionary<string,string> { {"foo","bar"} }
        };
    
        var ms = new MemoryStream();
    
        var slz = new DataContractSerializer(typeof(Foo));
        slz.WriteObject(ms, o,
            new Dictionary<string,string>
            {
                { "arr", "http://schemas.microsoft.com/2003/10/Serialization/Arrays" },
            });
    
        string data = Encoding.UTF8.GetString(ms.ToArray());
        Console.WriteLine(data);
    }
    
    public static class Extensions
    {
        public static void WriteObject(
            this DataContractSerializer serializer,
            Stream stream, object data,
            Dictionary<string,string> namespaces)
        {
            using (var writer = XmlWriter.Create(stream))
            {
                serializer.WriteStartObject(writer, data);
                foreach (var pair in namespaces)
                {
                    writer.WriteAttributeString("xmlns", pair.Key, null, pair.Value);
                }
                serializer.WriteObjectContent(writer, data);
                serializer.WriteEndObject(writer);
            }
        }
    }
    
    [DataContract]
    class Foo
    {
        [DataMember]
        public Dictionary<string,string> Prop;
    }
    

    Output:

    <?xml version="1.0" encoding="utf-8"?>
    <Foo xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
         xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://schemas.datacontract.org/2004/07/">
        <Prop>
            <arr:KeyValueOfstringstring>
                <arr:Key>foo</arr:Key>
                <arr:Value>bar</arr:Value>
            </arr:KeyValueOfstringstring>
        </Prop>
    </Foo>