【问题标题】:Namespace Prefixes with IXmlSerializable具有 IXmlSerializable 的命名空间前缀
【发布时间】:2014-04-28 22:52:11
【问题描述】:

对要使用的正确装饰器或可能需要的任何设计有点困惑。当序列化一个实现 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>

第二个是 XmlSerializer 的输出,如果删除了类的 IXmlSerializable 实现并且取消注释 XmlElement 装饰器。我知道返回的两个 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试试这个,看看这个属性模式是否允许你控制根元素上的命名空间。
  • 确实如此,我在问题中提到了这一点。如果您不使用 IXmlSerializable 并允许 .NET/XmlSerializer 处理序列化,那么它将它们放在根元素中。但是当然,您在 xml 序列化/反序列化中绝对失去了所有控制权(对于复杂对象使用 IXmlSerializable 有点意思)。
  • 仅供参考,您知道您的两个 XML 示例是相同的吗?
  • 是的,我在问题中也提到了这一点。不过,最好删除每个节点的冗余 xml 命名空间声明。因为它只是无缘无故变得冗长。

标签: c# xml serialization xml-namespaces ixmlserializable


【解决方案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 属性。

【讨论】:

    【解决方案2】:

    按照建议使用 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");
    }
    

    【讨论】:

      【解决方案3】:

      您需要设置 XmlWriterSetting 才能使其工作。请见下文

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

      【讨论】:

      • 好吧,我没想到要查看 XmlWriter(或它的设置)。我差点以为你在那里找到了答案。我看到的唯一与此相关的属性是 NamespaceHandling。但是,在将其设置为 OmitDuplicates 后,它仍会重写所有命名空间声明。你指的是这个吗?问题已用相关代码更新。
      猜你喜欢
      • 2019-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多