43

How do I remove XML namespaces from an object's XML representation serialized using DataContractSerializer?

That object needs to be serialized to a very simple output XML.

  • Latest & greatest - using .Net 4 beta 2
  • The object will never need to be deserialized.
  • XML should not have any xmlns:... namespace refs
  • Any subtypes of Exception and ISubObject need to be supported.
  • It will be very difficult to change the original object.

Object:

 [Serializable] 
 class MyObj
 {
     string str;
     Exception ex;
     ISubObject subobj;
 } 

Need to serialize into:

<xml>
  <str>...</str>
  <ex i:nil="true" />
  <subobj i:type="Abc">
     <AbcProp1>...</AbcProp1>
     <AbcProp2>...</AbcProp2>
  </subobj>
</xml>

I used this code:

private static string ObjectToXmlString(object obj)
{
    if (obj == null) throw new ArgumentNullException("obj");

    var serializer =
        new DataContractSerializer(
            obj.GetType(), null, Int32.MaxValue, false, false, null,
            new AllowAllContractResolver());

    var sb = new StringBuilder();
    using (var xw = XmlWriter.Create(sb, new XmlWriterSettings
    {
        OmitXmlDeclaration = true,
        NamespaceHandling = NamespaceHandling.OmitDuplicates,
        Indent = true
    }))
    {
        serializer.WriteObject(xw, obj);
        xw.Flush();

        return sb.ToString();
    }
}

From this article I adopted a DataContractResolver so that no subtypes have to be declared:

public class AllowAllContractResolver : DataContractResolver
{
    public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (!knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace))
        {
            var dictionary = new XmlDictionary();
            typeName = dictionary.Add(dataContractType.FullName);
            typeNamespace = dictionary.Add(dataContractType.Assembly.FullName);
        }
        return true;
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? Type.GetType(typeName + ", " + typeNamespace);
    }
}
Yuri Astrakhan
  • 8,808
  • 6
  • 63
  • 97

2 Answers2

47

You need to mark the classes you want to serialize with:

[DataContract(Namespace="")]

In that case, the data contract serializer will not use any namespace for your serialized objects.

Marc

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Some of the sub-objects are not under my control and I cannot change them. Is it possible to do it without changing the objects? – Yuri Astrakhan Dec 05 '09 at 10:36
  • No, sorry - you cannot remove an existing namespace - you have to be able to specify the namespace on the object itself. – marc_s Dec 05 '09 at 11:17
  • 2
    That can't be - what about all the system types? MS can't expect all types to be modifiable, there has to be some other way :( – Yuri Astrakhan Dec 05 '09 at 13:04
  • 2
    No, there doesn't. It doesn't actually make sense for you to be removing namespaces. – John Saunders Dec 06 '09 at 20:06
  • 2
    So far I have implemented my own XmlWriter (delegating to the real one) that skips namespaces. Not clean, but it works. I suspect that DataContractResolver is capable of solving this for me... Will keep the issue open to see if any new solutions come forth. – Yuri Astrakhan Dec 07 '09 at 15:28
  • This is actually printing `namespace = ""` in my xml :/ – Sinaesthetic Jul 25 '16 at 22:05
  • And dealing with namespaces is still a PITA several years later. Every program seems to interpret dealing with them differently, even .net apps vs. TSQL don't seem to play together well. – StingyJack Oct 17 '16 at 19:58
11

If you have your heart set on bypassing the default behavior (as I currently do), you create a custom XmlWriter that bypasses writing the namespace.

using System.IO;
using System.Xml;

public class MyXmlTextWriter : XmlTextWriter
{
  public MyXmlTextWriter(Stream stream)
    : base(stream, null)
  {
  }

  public override void WriteStartElement(string prefix, string localName, string ns)
  {
    base.WriteStartElement(null, localName, "");
  }
}

Then in your writer consumer, something like the following:

var xmlDoc = new XmlDocument();
DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
  using (var writer = new MyXmlTextWriter(ms))
  {
    serializer.WriteObject(writer, obj);
    writer.Flush();
    ms.Seek(0L, SeekOrigin.Begin);
    xmlDoc.Load(ms);
  }
}

And the output will have namespace declarations in it, but there will be no usages as such:

<TestObject xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Items xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <string>Item1</string>
    <string>Item2</string>
  </Items>
</TestObject>
John Saunders
  • 160,644
  • 26
  • 247
  • 397
leat
  • 1,418
  • 1
  • 15
  • 21
  • XmlTextWriter giving me a problem to pass setting through constructor compare to XmlWriter – Kelmen Oct 16 '15 at 10:56
  • 1
    Were you not tempted to override `WriteStartAttribute` to get rid of the declarations as well? – Carlos P Jan 18 '16 at 17:26
  • @CarlosP Maybe, but I needed it namespace free for easy XSLT on the server, so that was my driving reason. If I were integrating this in WebApi serialization for xml I would definitely use your suggestion for lower load times. – leat Jan 19 '16 at 18:28