سؤال

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.

هل كانت مفيدة؟

المحلول

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top