I have a following class definition
[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 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);
I expect following output:
<?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>
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.
When using prefixes in XML, a namespace for the prefix must be defined. The namespace can be defined by an xmlns attribute in the start tag of an element. The namespace declaration has the following syntax. xmlns:prefix="URI".
Adding a namespace prefix to an elementThe XQuery expression in the following SELECT statement adds a namespace prefix to an element. The XQuery expression uses fn:QName to create a QName with a namespace binding. The XQuery let clause creates an empty element with name emp and namespace http://example.com/new .
Xml. Serialization namespace contains several Attribute classes that can be applied to members of a class. For example, if a class contains a member that will be serialized as an XML element, you can apply the XmlElementAttribute attribute to the member.
URL namespaces allow you to uniquely reverse named URL patterns even if different applications use the same URL names. It's a good practice for third-party apps to always use namespaced URLs. Similarly, it also allows you to reverse URLs if multiple instances of an application are deployed.
This article describes how to control namespace prefixes when serializing an XML tree in C# and Visual Basic. In many situations, it's not necessary to control namespace prefixes. However, certain XML programming tools require it.
In such a case, it's important that the document be serialized with those prefixes. This is a common reason for controlling namespace prefixes. Another reason is that you want users to edit the XML document manually, and you want to create namespace prefixes that are convenient for the user to type.
If you declare the namespaces with specific prefixes, LINQ to XML will attempt to honor the namespace prefixes when serializing. To create an attribute that declares a namespace with a prefix, you create an attribute where the namespace of the name of the attribute is Xmlns, and the name of the attribute is the namespace prefix.
This example shows how to import the XML namespace prefix ns and use it in an XML literal and XML axis properties. ' Place Imports statements at the top of your program. Imports <xmlns:ns="http://SomeNamespace"> Module Sample1 Sub SampleTransform () ' Create test by using a global XML namespace prefix.
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With