1

I am encountering the following problem. Whenever I use the default XML serialization in my C# class, the namespaces xsi and xsd are automatically added by the .NET serialization engine. However when the serialization is defined through IXmlSerializable, the namespaces are not added.

Example: this code:

class Program
{
    static void Main(string[] args)
    {
        OutputSerialized(new Outer() { Inner = new Inner() });
        OutputSerialized(new OuterCustom() { Inner = new Inner() });
    }

    static void OutputSerialized<T>(T t)
    {
        var sb = new StringBuilder();
        using (var textwriter = new StringWriter(sb))
            new XmlSerializer(typeof(T)).Serialize(textwriter, t);
        Console.WriteLine(sb.ToString());
    }
}

[Serializable] public class Inner { }

[Serializable] public class Outer { public Inner Inner { get; set; } }

public class OuterCustom : IXmlSerializable
{
    public Inner Inner;

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteStartElement("Inner");
        new XmlSerializer(typeof(Inner)).Serialize(writer, Inner);
        writer.WriteEndElement();
    }

    public System.Xml.Schema.XmlSchema GetSchema() { return null; }
    public void ReadXml(System.Xml.XmlReader reader) { /**/ }
}

produces the following output:

<?xml version="1.0" encoding="utf-16"?>
<Outer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Inner />
</Outer>
<?xml version="1.0" encoding="utf-16"?>
<OuterCustom>
  <Inner>
    <Inner xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
  </Inner>
</OuterCustom>

You can see that the OuterCustom's serialized form is missing the xsd and xsi namespaces.

How can I make my OuterCustom behave the same way as Outer? Am I missing something in my code? Am I doing the custom serialization wrong?

There are a lot of questions here on SO about how to get rid of the additional namespaces, but it seems that no one asked about how to get them back.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • There is no difference between 2 XML samples from XML point of view... Are you trying to do some string/regex searches on XML? – Alexei Levenkov Nov 27 '13 at 20:19
  • @Alexei Levenkov: Well, I am trying to beautify the XML output: in the actual code I've got a lot of `Inner` classes, each of them containing additional namespaces (which looks pretty ugly). In any case, I wonder why does XML serializer do different things in the two cases, they seem to be "identical". – Vlad Nov 27 '13 at 20:21
  • Have your tried to manually write xmlns attributes on `OuterCustom` with [WriteAttributeString](http://msdn.microsoft.com/en-us/library/73z46xs1%28v=vs.110%29.aspx)? – Alexei Levenkov Nov 27 '13 at 20:29
  • @Alexei Levenkov: just tried it. Seems to help, but I need to hardcode the URIs like `http://www.w3.org/2001/XMLSchema` -- is this a really good way? – Vlad Nov 27 '13 at 20:34
  • Yes - these are well known namespaces defined by W3C for XML. Perfectly safe to hard code (there may be even existing constants for them). – Alexei Levenkov Nov 27 '13 at 20:37
  • @Alexei Levenkov: well, there is another problem with this approach: extra attributes are written when OuterCustom is used inside some other element. Example: http://ideone.com/11u23u – Vlad Nov 27 '13 at 21:08
  • If I'd be tasked with beautification of XML I'd just post-process whole result and rewrite XML to my liking instead of trying to convince serialization code to do what I what. All versions of XML you have so far are equivalent and equally readable to me. You may dig through XmlWriter class and see if you can get more info (like LookupPrefix) to match XML to your beautification rules. – Alexei Levenkov Nov 27 '13 at 21:45
  • @Alexei Levenkov: using LookupPrefix is a good suggestion, I cannot find a case which works not as expected: http://ideone.com/qyZh6t. Would you mind summing up the discussion as an answer? – Vlad Nov 27 '13 at 22:42

1 Answers1

2

First of all - there is no difference between mentioned versions of XML for compliant XML parser. It does not really matter if/how/where namespace prefixes defined as long as nodes have correct namespaces associated.

As this question is explicitly about style where to put namespace declaration one makes own call what is nice and what is not. In this particular case goal is to avoid duplicated xmlns attributes and move them higher in the tree.

One can write xmlns attributes wherever needed as long they don't conflict on the same node. Use XmlWriter.WriteAttributeString method to add them:

writer.WriteAttributeString(
  "xmlns", "prefix", null, "urn:mynamespace");

To avoid duplicate declarations - check if prefix already defined for given namespace using XmlWriter.LookupPrefix

Sample code to ensure xsd and xsi prefixes (by Vlad):

const string xsiNamespace = System.Xml.Schema.XmlSchema.InstanceNamespace;
const string xsdNamespace = System.Xml.Schema.XmlSchema.Namespace;

public static void EnsureDefaultNamespaces(System.Xml.XmlWriter writer)
{
   if (writer.LookupPrefix(xsiNamespace) == null)
       writer.WriteAttributeString("xmlns", "xsi", null, xsiNamespace);
   if (writer.LookupPrefix(xsdNamespace) == null)
       writer.WriteAttributeString("xmlns", "xsd", null, xsdNamespace);
}

Alternative approach if more changes desired to achieve nice looking XML is to allow serialization to finish and than process XML to adjust prefixes definitions (i.e. normalize to desired prefixes, collect all namespaces and define all on top). One can either read XML with C# code or even create XSLT transformation.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179