7

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>
dbc
  • 104,963
  • 20
  • 228
  • 340
rekire
  • 47,260
  • 30
  • 167
  • 264
  • [How to serialize an object to XML without getting xmlns=“…”?](http://stackoverflow.com/questions/258960/how-to-serialize-an-object-to-xml-without-getting-xmlns) <-- This contains what you want – Markus Jarderot Jun 27 '12 at 07:46
  • @MarkusJarderot no not really. That seems to be a way to get rid of the namespaces in generall. WCF contracts needs that (so far I know)! I just want to avoid that there are severial redefinitions. – rekire Jun 27 '12 at 07:48

2 Answers2

12
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>
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • That looks perfect. Just a pity that there is not attribute which adds that declatation for me. – rekire Jun 27 '12 at 08:20
  • That seems to be a suitable solution. Thank you again! With the `DataContractSerializer` works everything fine. But the `DataContractJsonSerializer` throws a SerializationException talking about a `DataContractResolver` or that I should use the `KnownTypeAttribute`. Why does the `DataContractJsonSerializer` want that but the `DataContractSerializer` not? – rekire Jun 27 '12 at 08:57
  • The extension method in `Extensions` is only usable for the `DataContractSerializer`. – Markus Jarderot Jun 27 '12 at 09:02
  • I know that. I was just testing if my structure can be also serialited in the JSON format. (Without using your code) – rekire Jun 27 '12 at 09:05
  • You could add a dummy extension method into `Extensions` for `DataContractJsonSerializer`. – Markus Jarderot Jun 27 '12 at 09:07
2

I successfully used the solution described here: http://blogs.msdn.com/b/youssefm/archive/2009/07/24/optimizing-away-repeat-xml-namespace-declarations-with-datacontractserializer.aspx

You basically create a behavior which adds the namespaces to the root element for you.

From the article:

Just create a serializer that inherits from XmlObjectSerializer that uses a DataContractSerializer for all of its methods, except for the fact that it registers additional namespaces at the top level. Then create a behavior that derives from DataContractSerializerOperationBehavior with a CreateSerializer method that returns the XmlObjectSerializer you just created and plug in the behavior.

In case you want to do it in Silverlight, you can also use the solution described here: http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/24/wcf-extensibility-custom-serialization-in-silverlight.aspx

RobSiklos
  • 8,348
  • 5
  • 47
  • 77