使用IXmlSerializable的命名空间前缀

7

对于正确的装饰器使用或者所需的设计有些困惑。当序列化一个实现IXmlSerializable接口的类时,是否有一种方法可以在XmlRoot元素中包含命名空间和它的前缀?

示例的类定义。

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; }
}

当通过以下方式序列化时:

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

输出结果为:

<?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>

我期望的输出是:
<?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>

第二种情况是如果删除类的IXmlSerializable实现并取消注释XmlElement装饰器,则XmlSerializer的输出。我知道返回的两个Xml文档都是有效的,但尝试删除冗余的命名空间声明会很好。我会假设这样的事情是可能的,因为IXmlSerializable旨在更好地控制类的序列化/反序列化。
更新:一个非常有趣的答案建议修改XmlWriterSettings!看起来通过修改NamespaceHandling属性可以清除所有问题。然而,这仍然导致每个成员的命名空间被重写。完整的序列化代码如下:
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();

尝试一下不使用 IXmlSerializable,看看这个属性模式是否允许您控制根元素上的命名空间。 - John Saunders
它确实会这样做,我在问题中提到了。如果您不使用IXmlSerializable并允许.NET/XmlSerializer处理序列化,则它会将它们放在根元素中。但是,当然,在xml序列化/反序列化方面,您将失去绝对的控制(这是使用IXmlSerializable处理复杂对象的重点)。 - Nicholi
请注意,您的两个XML示例是相同的,您知道吗? - John Saunders
是的,我在问题中也提到了这一点。不过,如果每个节点都有冗余的XML命名空间声明,将其删除会很好。因为这只是毫无意义地增加了冗长。 - Nicholi
3个回答

1

我只是在WriteXml中添加了一个字符串属性。它起作用了!

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);
    }

并从类中删除[XmlRoot("Classy", Namespace = XML_NS)]xmlsn属性。


0

使用建议的WriteAttributeString并不完全正确。以下是如何正确使用的示例:

在WriteXml方法的开头添加类似以下内容:

    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");
}

-1

我之前没有想过查看XmlWriter(或其设置)。我差点以为你已经有了答案。我所看到的唯一与此相关的属性是NamespaceHandling。然而,即使将其设置为OmitDuplicates,它仍会重写所有命名空间声明。这就是你所指的吗?问题已更新并附上相关代码。 - Nicholi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接