C# Xml serialization, collection and root element C# Xml serialization, collection and root element xml xml

C# Xml serialization, collection and root element


Ok, here is my final solution (hope it helps someone), that can serialize a plain array, List<>, HashSet<>, ...

To achieve this, we'll need to tell the serializer what root node to use, and it's kind of tricky...

1) Use 'XmlType' on the serializable object

[XmlType("link")]public class LinkFinalVersion{    [XmlAttribute("href")]    public string Url { get; set; }    [XmlAttribute("rel")]    public string Relationship { get; set; }}

2) Code a 'smart-root-detector-for-collection' method, that will return a XmlRootAttribute

private XmlRootAttribute XmlRootForCollection(Type type){    XmlRootAttribute result = null;    Type typeInner = null;    if(type.IsGenericType)    {        var typeGeneric = type.GetGenericArguments()[0];        var typeCollection = typeof (ICollection<>).MakeGenericType(typeGeneric);        if(typeCollection.IsAssignableFrom(type))            typeInner = typeGeneric;    }    else if(typeof (ICollection).IsAssignableFrom(type)        && type.HasElementType)    {        typeInner = type.GetElementType();    }    // yeepeeh ! if we are working with a collection    if(typeInner != null)    {        var attributes = typeInner.GetCustomAttributes(typeof (XmlTypeAttribute), true);        if((attributes != null)            && (attributes.Length > 0))        {            var typeName = (attributes[0] as XmlTypeAttribute).TypeName + 's';            result = new XmlRootAttribute(typeName);        }    }    return result;}

3) Push that XmlRootAttribute into the serializer

// hack : get the XmlRootAttribute if the item is a collectionvar root = XmlRootForCollection(type);// create the serializervar serializer = new XmlSerializer(type, root);

I told you it was tricky ;)


To improve this, you can :

A) Create a XmlTypeInCollectionAttribute to specify a custom root name (If the basic pluralization does not fit your need)

[XmlType("link")][XmlTypeInCollection("links")]public class LinkFinalVersion{}

B) If possible, cache your XmlSerializer (in a static Dictionary for example).

In my testing, instanciating a XmlSerializer without the XmlRootAttributes takes 3ms.If you specify an XmlRootAttribute, it takes around 80ms (Just to have a custom root node name !)


XmlSerializer should be able to do what you need, but it is highly dependent on the initial structure and setup. I use it in my own code to generate remarkably similar things.

public class Links<Link> : BaseArrayClass<Link> //use whatever base collection extension you actually need here{    //...stuff...//}public class Link{    [XmlAttribute("href")]    public string Url { get; set; }    [XmlAttribute("rel")]    public string Relationship { get; set; }}

now, serializing the Links class should generate exactly what you are looking for.

The problem with XmlSerializer is when you give it generics, it responds with generics. List implemets Array somewhere in there and the serialized result will nearly always be ArrayOf<X>. To get around that you can name the property, or the class root. The closes to what you need is probably the Second Version from your examples. Im assuming you attempted direct serialization of an object List Links. That wouldn't work because you didn't specify the root node. Now, a similar approach can be found here. In this one they specify the XmlRootAttribute when declaring the serializer. yours would look like this:

XmlSerializer xs = new XmlSerializer(typeof(List<Link>), new XmlRootAttribute("Links"));


Here you go...

 class Program{    static void Main(string[] args)    {        Links ls = new Links();        ls.Link.Add(new Link() { Name = "Mike", Url = "www.xml.com" });        ls.Link.Add(new Link() { Name = "Jim", Url = "www.xml.com" });        ls.Link.Add(new Link() { Name = "Peter", Url = "www.xml.com" });        XmlSerializer xmlSerializer = new XmlSerializer(typeof(Links));        StringWriter stringWriter = new StringWriter();        xmlSerializer.Serialize(stringWriter, ls);        string serializedXML = stringWriter.ToString();        Console.WriteLine(serializedXML);        Console.ReadLine();    }}[XmlRoot("Links")]public class Links{    public Links()    {        Link = new List<Link>();    }    [XmlElement]    public List<Link> Link { get; set; }}[XmlType("Link")]public class Link{    [XmlAttribute("Name")]    public string Name { get; set; }    [XmlAttribute("Href")]    public string Url { get; set; }}