Reputation: 163
Suppose I have a class:
using System.Xml;
using System.Xml.Serialization;
class Foo {
[XmlElement(ElementName="Bar")]
public string Bar {get; set;}
}
And now I want to serialize it. But. I want to specify different namespaces, On one occasion I want my XML look like this:
<Foo xmlns:v1="http://site1">
<v1:Bar />
</Foo>
And on the other one - like this:
<Foo xmlns:v2="http://site2">
<v2:Bar />
</Foo>
I know I need to specify the namespace in the XmlElement attribute, but this is exactly what I want to avoid. I could, of course, make 2 different classes which are identical except the field attributes, but this feels wrong somehow. Is there any way I could force the XmlSerializer use the namespace I choose at runtime?
Upvotes: 2
Views: 891
Reputation: 1064004
Yes; XmlAttributeOverrides
:
static void Main()
{
var obj = new Foo { Bar = "abc" };
GetSerializer("http://site1").Serialize(Console.Out, obj);
Console.WriteLine();
GetSerializer("http://site2").Serialize(Console.Out, obj);
}
static XmlSerializer GetSerializer(string barNamespace)
{
var ao = new XmlAttributeOverrides();
var a = new XmlAttributes();
a.XmlElements.Add(new XmlElementAttribute { Namespace = barNamespace });
ao.Add(typeof(Foo), nameof(Foo.Bar), a);
return new XmlSerializer(typeof(Foo), ao);
}
However!!!
When you do this, it generates an additional assembly in memory each time; you must cache and re-use the serializer instances - usually via a concurrent dictionary or similar. For example:
private static readonly ConcurrentDictionary<string, XmlSerializer>
s_serializersByNamespace = new();
static XmlSerializer GetSerializer(string barNamespace)
{
if (!s_serializersByNamespace.TryGetValue(barNamespace, out var serializer))
{
lock (s_serializersByNamespace)
{
// double-checked, avoid dups
if (!s_serializersByNamespace.TryGetValue(barNamespace, out serializer))
{
var ao = new XmlAttributeOverrides();
var a = new XmlAttributes();
a.XmlElements.Add(new XmlElementAttribute { Namespace = barNamespace });
ao.Add(typeof(Foo), nameof(Foo.Bar), a);
serializer = new XmlSerializer(typeof(Foo), ao);
s_serializersByNamespace[barNamespace] = serializer;
}
}
}
return serializer;
}
Note: if you want the specific xmlns
control too, that's XmlSerializerNamespaces
:
var obj = new Foo { Bar = "abc" };
var ns = new XmlSerializerNamespaces();
ns.Add("v1", "http://site1");
GetSerializer("http://site1").Serialize(Console.Out, obj, ns);
Console.WriteLine();
ns = new XmlSerializerNamespaces();
ns.Add("v2", "http://site2");
GetSerializer("http://site2").Serialize(Console.Out, obj, ns);
Upvotes: 3