Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 845
rekire Avatar asked Jun 27 '12 07:06

rekire


2 Answers

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>
like image 100
Markus Jarderot Avatar answered Sep 19 '22 09:09

Markus Jarderot


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

like image 22
RobSiklos Avatar answered Sep 18 '22 09:09

RobSiklos