How to add namespace prefix for IXmlSerializable type

↘锁芯ラ 提交于 2019-12-01 15:12:44

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.

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.

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.

The code below

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

results in

<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">.

Person

[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();       
    }
}
John Garrard

Try the following.

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

Do you need to use XmlSerializer? If not, try following code:

Person.cs

Add new method:

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

Usage

var person = new Person { FirstName = "John", LastName = "Doe" };
person.Serialize(new XmlTextWriter(Console.Out));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!