如何为 IXmlSerializable 类型添加命名空间前缀 [英] How to add namespace prefix for IXmlSerializable type

查看:21
本文介绍了如何为 IXmlSerializable 类型添加命名空间前缀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下类定义

[XmlRoot(ElementName = "person",Namespace = "MyNamespace")]
public class Person : IXmlSerializable
{
    public string FirstName { get; set; }
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get
        {
            var xmlSerializerNamespaces = new XmlSerializerNamespaces();
            xmlSerializerNamespaces.Add("My", "MyNamespace");
            return xmlSerializerNamespaces;
        }
    }

    public string LastName { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    /// <exception cref="NotSupportedException"/>
    public void ReadXml(XmlReader reader)
    {
        throw new NotSupportedException();
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteElementString("firstName",FirstName);
        writer.WriteElementString("lastName", LastName);
    }
}

an 我想用 MyNamespace 的 My: 前缀序列化它,所以当我调用代码时

an I want to serialize it with My: prefix for MyNamespace, so when I call code

var xmlSerializer = new XmlSerializer(typeof(Person));
var person = new Person
            { FirstName = "John",LastName = "Doe"};            
xmlSerializer.Serialize(Console.Out, person, person.Namespaces);

我希望得到以下输出:

<?xml version="1.0" encoding="ibm850"?>
<My:person xmlns:My="MyNamespace">
    <My:firstName>John</My:firstName>
    <My:lastName>Doe</My:lastName>
</My:person>

但我得到的不是它,而是以下输出:

But instead of it I am getting following output:

<?xml version="1.0" encoding="ibm850"?>
<person xmlns="MyNamespace">
  <firstName>John</firstName>
  <lastName>Doe</lastName>
</person>

我知道当我使用 SerializableAttribute 属性而不是从 IXmlSerializable 继承时,写前缀是有效的,但是我在项目中的类要复杂得多,我不能使用默认 XmlSerializer.

I know that writing prefixes works when I use SerializableAttribute attribute and not inherit from IXmlSerializable, but my class in the project is much more complex and I can't use default XmlSerializer.

推荐答案

XmlSerializer 类型不提供任何开箱即用的解决方案.
如果您真的需要使用 XmlSerializer,您最终会得到一个自定义的 XmlSerializer 实现,它不是很开放扩展.出于这个原因,下面的实现更像是一个概念证明,只是为了给你一个想法或一个起点.
为简洁起见,我省略了任何错误处理,仅关注您问题中的 Person 类.还有一些工作要做来处理任何嵌套的复杂属性.

The XmlSerializer type doesn't offer anything out-of-the-box to handle this.
If you really need to use XmlSerializer, you are going to end up with a custom XmlSerializer implementation, which isn't quite open to extend. For this reason the implementation below is more a proof of concept, just to give you an idea or a starting point.
For brevity I have omitted any error handling and only focussed on the Person class in your question. There's still some work to do to handle any nested complex properties.

由于 Serialize 方法不是 virtual,我们必须隐藏它们.主要思想是将所有重载定向到具有自定义实现的单个重载.

As the Serialize methods are not virtual we'll have to shadow them. The main idea is to direct all overloads to a single one having the custom implementation.

由于自定义,在通过指定要应用的 xml 命名空间为其属性编写 xml 元素时,我们必须在 Person 类中更加明确.

Because of the customization, we'll have to be more explicit in the Person class when writing the xml elements for its properties by specifying the xml namespace to be applied.

下面的代码

PrefixedXmlSerializer xmlSerializer = new PrefixedXmlSerializer(typeof(Person));
Person person = new Person { 
    FirstName = "John", 
    LastName = "Doe" 
    };
xmlSerializer.Serialize(Console.Out, person, person.Namespaces);

结果

<My:person xmlns:My="MyNamespace">
    <My:firstName>John</My:firstName>
    <My:lastName>Doe</My:lastName>
</My:person>

这一切是否可以接受由您来考虑.
最后, 等于 .

It's up to you to consider whether this all is acceptable.
In the end, <My:person xmlns:My="MyNamespace"> equals <person xmlns="MyNamespace">.

[XmlRoot(ElementName = "person", Namespace = NAMESPACE)]
public class Person : IXmlSerializable
{
    private const string NAMESPACE = "MyNamespace";

    public string FirstName { get; set; }

    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get
        {
            var xmlSerializerNamespaces = new XmlSerializerNamespaces();
            xmlSerializerNamespaces.Add("My", NAMESPACE);                
            return xmlSerializerNamespaces;
        }
    }

    public string LastName { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    /// <exception cref="NotSupportedException"/>
    public void ReadXml(XmlReader reader)
    {
        throw new NotSupportedException();
    }   

    public void WriteXml(XmlWriter writer)
    {
        // Specify the xml namespace.
        writer.WriteElementString("firstName", NAMESPACE, FirstName);
        writer.WriteElementString("lastName", NAMESPACE, LastName);
    }
}

PrefixedXmlSerializer

public class PrefixedXmlSerializer : XmlSerializer
{
    XmlRootAttribute _xmlRootAttribute;


    public PrefixedXmlSerializer(Type type) : base(type)
    {
        this._xmlRootAttribute = type.GetCustomAttribute<XmlRootAttribute>();        
    }


    public new void Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
    {
        // Out-of-the-box implementation.
        XmlTextWriter xmlTextWriter = new XmlTextWriter(textWriter);
        xmlTextWriter.Formatting = Formatting.Indented;
        xmlTextWriter.Indentation = 2;

        // Call the shadowed version. 
        this.Serialize(xmlTextWriter, o, namespaces, null, null);
    }


    public new void Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
    {
        // Lookup the xml namespace and prefix to apply.
        XmlQualifiedName[] xmlNamespaces = namespaces.ToArray();                                
        XmlQualifiedName xmlRootNamespace =
            xmlNamespaces
                .Where(ns => ns.Namespace == this._xmlRootAttribute.Namespace)
                .FirstOrDefault();

        // Write the prefixed root element with its xml namespace declaration.
        xmlWriter.WriteStartElement(xmlRootNamespace.Name, this._xmlRootAttribute.ElementName, xmlRootNamespace.Namespace);            

        // Write the xml namespaces; duplicates will be taken care of automatically.
        foreach (XmlQualifiedName xmlNamespace in xmlNamespaces)
        {
            xmlWriter.WriteAttributeString("xmlns", xmlNamespace.Name , null, xmlNamespace.Namespace);
        }

        // Write the actual object xml.
        ((IXmlSerializable)o).WriteXml(xmlWriter);

        xmlWriter.WriteEndElement();       
    }
}

这篇关于如何为 IXmlSerializable 类型添加命名空间前缀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆