简体   繁体   中英

C# XmlSerializer Serialize the same class with different namespaces

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?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM