7

Bit confused on the proper decorators to use, or whatever design might be necessary. When serializing a class which is implementing IXmlSerializable is there a way to include the namespace and its prefix in the XmlRoot element?

Class definition for example.

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
[XmlRoot("Classy", Namespace = XML_NS)]
public class TestClass : IXmlSerializable
{
    private const string XML_PREFIX = ""; // default namespace
    private const string XML_NS = "www.123.com";
    private const string XML_MEMBER_PREFIX = "me";
    private const string XML_MEMBER_NS = "member.com";

    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces xmlsn {
        get {
            XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
            xsn.Add(XML_PREFIX, XML_NS);
            xsn.Add(XML_MEMBER_PREFIX, XML_MEMBER_NS);
            return xsn;
        }
    }

    void IXmlSerializable.WriteXml(XmlWriter writer) {
        writer.WriteElementString(XML_MEMBER_PREFIX, "Member1.5",
                                  XML_MEMBER_NS, Member1);
        writer.WriteElementString(XML_MEMBER_PREFIX, "Member2.5",
                                  XML_MEMBER_NS, Member2.ToString());
        writer.WriteElementString(XML_PREFIX, "Member3.5", XML_NS, Member3);
    }

    //[XmlElement(ElementName = "Member1.5", Namespace = XML_MEMBER_NS)]
    public string Member1 {
        get { return "init"; }
        set { ; }
    }
    //[XmlElement(ElementName = "Member2.5", Namespace = XML_MEMBER_NS)]
    public int Member2 {
        get { return 3; }
        set { ; }
    }
    //[XmlElement(ElementName = "Member3.5", Namespace = XML_NS)]
    public string Member3 {
        get { return "default namespace"; }
        set { ; }
    }

    // ignore ReadXml/GetSchema
    XmlSchema IXmlSerializable.GetSchema() { return null; }
    void IXmlSerializable.ReadXml(XmlReader reader) { return; }
}

When serialized via:

TestClass tc = new TestClass();
XmlSerializer ser = new XmlSerializer(typeof(TestClass)); 
ser(writer, tc, tc.xmlsn); // just a plain XmlWriter to Console.Out

the output is

<?xml version="1.0" encoding="utf-8"?>
<Classy xmlns="www.123.com">
    <me:Member1.5 xmlns:me="member.com">init</me:Member1.5>
    <me:Member2.5 xmlns:me="member.com">3</me:Member2.5>
    <Member3.5>default namespace</Member3.5>
</Classy>

The output I was expecting was:

<?xml version="1.0" encoding="utf-8"?>
<Classy xmlns:me="member.com" xmlns="www.123.com">
    <me:Member1.5>init</me:Member1.5>
    <me:Member2.5>3</me:Member2.5>
    <Member3.5>default namespace</Member3.5>
</Classy>

The second is the output of XmlSerializer if the class's implementation of IXmlSerializable is removed and you uncomment the XmlElement decorators. I know both of the Xml documents returned are valid, but it would be nice to try and remove the redundant namespace declarations. I would assume such a thing is possible, since IXmlSerializable is meant to give greater control over how your class is serialized/deserialized.

Update: A very interesting answer suggested modifying the XmlWriterSettings! It looked like this would clear everything up by modifying the NamespaceHandling property. However this still results in the namespaces being rewritten for each member. Full serialization code below:

XmlSerializer ser = new XmlSerializer(typeof(TestClass));
TestClass tc = new TestClass();

XmlWriterSettings xmlSet = new XmlWriterSettings();
xmlSet.Encoding = System.Text.Encoding.UTF8;
xmlSet.Indent = true;
xmlSet.NamespaceHandling = NamespaceHandling.OmitDuplicates;
XmlWriter writer = XmlWriter.Create(Console.Out, xmlSet);

ser.Serialize(writer, tc); // with/without tc.xmlsn same result
writer.Flush();
writer.Close();
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Nicholi
  • 1,083
  • 19
  • 33
  • Try this without `IXmlSerializable` to see if this attribute pattern allows you to control the namespaces on the root element. – John Saunders Oct 22 '11 at 19:58
  • It does, I mentioned that in the question. If you don't use IXmlSerializable and allow .NET/XmlSerializer to handle the serialization then it puts them in the root element. But then of course you lose absolutely all control in the xml serialization/deserialization (sort of the point to using IXmlSerializable for complex objects). – Nicholi Oct 22 '11 at 23:43
  • FYI, are you aware that your two XML examples are identical? – John Saunders Oct 22 '11 at 23:48
  • Yes, I also mentioned that in the question. It would be nice to remove the redundant xml namespace declarations for each and every node though. As it just gets verbose for no reason whatsoever. – Nicholi Oct 23 '11 at 02:08

3 Answers3

1

I simply added an String Attribute to WriteXml. It worked!

void IXmlSerializable.WriteXml(XmlWriter writer) {
        writer.WriteAttributeString("xmlns:me", "member.com");
        writer.WriteAttributeString("xmlns", "www.123.com");
        writer.WriteElementString(XML_MEMBER_PREFIX, "Member1.5",
                                  XML_MEMBER_NS, Member1);
        writer.WriteElementString(XML_MEMBER_PREFIX, "Member2.5",
                                  XML_MEMBER_NS, Member2.ToString());
        writer.WriteElementString(XML_PREFIX, "Member3.5", XML_NS, Member3);
    }

and remove [XmlRoot("Classy", Namespace = XML_NS)] and the xmlsn property from the class.

alisabzevari
  • 8,008
  • 6
  • 43
  • 67
0

Using the WriteAttributeString as suggested is not completely correct. Here is an example on how to use it correctly:

In the WriteXml method at the beginning add something like this:

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
        writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
}
gigi
  • 796
  • 9
  • 21
-1

You need to set XmlWriterSetting for it to work. see below pls

http://msdn.microsoft.com/en-us/library/system.xml.xmlwritersettings.aspx

  • Well I hadn't thought to look at the XmlWriter (or it's settings). I almost thought you had the answer there. The only property I see related to this is NamespaceHandling. However after setting it to OmitDuplicates, it still rewrites all the namespace declarations. Is this what you were referring to? Question has been updated with relevant code. – Nicholi Oct 05 '11 at 17:24