如何为IXmlSerializable类型添加名称空间前缀

时间:2018-07-30 08:45:34

标签: xml-serialization xml-namespaces prefix 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);
    }
}

我想使用MyNamespace的 My:前缀对其进行序列化,所以当我调用代码

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>

但我得到的却是以下输出:

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

我知道,当我使用 SerializableAttribute 属性而不是从 IXmlSerializable 继承时,写前缀是可行的,但是我在项目中的类复杂得多,我无法使用默认为XmlSerializer。

3 个答案:

答案 0 :(得分:1)

XmlSerializer类型没有提供任何开箱即用的内容来处理此问题。
如果您确实需要使用XmlSerializer,那么您将得到一个自定义的XmlSerializer实现,该实现尚未扩展。因此,下面的实现更多是概念证明,只是为了给您一个想法或起点。
为简洁起见,我省略了任何错误处理,仅关注您问题中的Person类。还有一些工作要做,以处理任何嵌套的复杂属性。

由于Serialize方法不是virtual,因此我们必须对其进行阴影处理。主要思想是将所有重载定向到具有自定义实现的单个重载。

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

下面的代码

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>

这取决于您自己是否可以接受。
最后,<My:person xmlns:My="MyNamespace">等于<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();       
    }
}

答案 1 :(得分:0)

尝试以下操作。

public void WriteXml(XmlWriter writer)
{
    writer.WriteAttributeString("xmlns", "my", null, "MyNamespace");
    writer.WriteElementString("firstName", FirstName);
    writer.WriteElementString("lastName", LastName);
}

答案 2 :(得分:0)

您需要使用XmlSerializer吗?如果没有,请尝试以下代码:

Person.cs

添加新方法:

public void Serialize(XmlWriter writer)
{
    writer.WriteStartDocument();
    writer.WriteStartElement("My", "Person", "MyNamespace");
    writer.WriteElementString("My", "FirstName", "MyNamespace", FirstName);
    writer.WriteElementString("My", "LastName", "MyNamespace", LastName);
    writer.WriteEndElement();
    writer.WriteEndDocument();
}

用法

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