Customizing the XML for collections with XmlSerializer and DataContractSerializer
One of the most common requests I get about serialization is to explain how to get collections to serialize out one way or another to XML. Specifically, developers are interested in being able to manipulate the hierarchy level of the collection within their XML and the names of the various XML elements. In today’s post, I’ll go over how to do these things with these two XML serializers.
First, let’s define a simple collection type:
public class Library
{
public string Librarian { get; set; }
public List<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
Now, by default, you should be able to serialize this type out with XmlSerializer and DataContractSerializer to get XML that looks something like this:
<Library xmlns="https://schemas.datacontract.org/2004/07/" xmlns:i="https://www.w3.org/2001/XMLSchema-instance">
<Books>
<Book>
<Title>Welcome to the Monkey House</Title>
</Book>
<Book>
<Title>American Gods</Title>
</Book>
<Book>
<Title>Bel Canto</Title>
</Book>
</Books>
<Librarian>Kelly</Librarian>
</Library>
Notice that you have two levels of hierarchy to represent the List<Book> collection. You have the <Books> tag and then you have a <Book> tag for every book in the collection.
Changing the level of hierarchy
In some cases though, you may not want the extra <Books> tag. To eliminate the tag with XmlSerializer, simply update the Books member with an XmlElementAttribute that specifies the name you want to use for each item:
public class Library
{
public string Librarian { get; set; }
[XmlElement("Book")]
public List<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
You should now notice that the instance is being serializer differently without the extra <Books> tag:
<Library xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
<Librarian>Kelly</Librarian>
<Book>
<Title>Welcome to the Monkey House</Title>
</Book>
<Book>
<Title>American Gods</Title>
</Book>
<Book>
<Title>Bel Canto</Title>
</Book>
</Library>
Note, however, that this is only an option with XmlSerializer. DataContractSerializer does not support serializing out a collection as an unwrapped list of elements. In my first post, I indicated that XmlSerializer allows for more flexible XML than DataContractSerializer. This is just one of the numerous ways in which XmlSerializer can produce XML that DataContractSerializer simply cannot.
Changing the names of the XML tags
If you are happy with keeping your collection wrapped within an element, then you still may want to change the names of the XML tags. To do so with XmlSerializer, you can use XmlArrayAttribute and XmlArrayItemAttribute to configure the names. XmlArray defines the wrapper name and XmlArrayItem defines the item name:
public class Library
{
public string Librarian { get; set; }
[XmlArray("FavoriteBooks")]
[XmlArrayItem("FavoriteBook")]
public List<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
To change the names with DataContractSerializer, you will need to define a new type that derives from List<Book> and is marked with a CollectionDataContractAttribute. The DataMember name defines the wrapper name and the ItemName on the CollectionDataContract defines the item name:
[DataContract]
public class Library
{
[DataMember]
public string Librarian { get; set; }
[DataMember(Name="FavoriteBooks")]
public BookList Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
[CollectionDataContract(ItemName="FavoriteBook")]
public class BookList : List<Book> { }
In both of these cases, the XML that’s serialized out should look something like this:
<Library xmlns="https://schemas.datacontract.org/2004/07/" xmlns:i="https://www.w3.org/2001/XMLSchema-instance">
<FavoriteBooks>
<FavoriteBook>
<Title>Welcome to the Monkey House</Title>
</FavoriteBook>
<FavoriteBook>
<Title>American Gods</Title>
</FavoriteBook>
<FavoriteBook>
<Title>Bel Canto</Title>
</FavoriteBook>
</FavoriteBooks>
<Librarian>Kelly</Librarian>
</Library>
Comments
Anonymous
June 12, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
September 15, 2015
Changing the level of hierarchy was a gold nugget, needed this for a specific architecture and glad I found it. Thank you