I don't work with XML Serialization much, but every now and again I find myself working on something that requires a little XML love. I recently answered a StackOverflow question regarding the subject.
What's covered
- Understanding the parts of an XML document
- Setting up a POCO class to mirror an XML document
- Deserialization of an XML string to a c# class
- Serialization of a prepared C# class to an XML string
The Anatomy of XML
Let's start by pulling apart an XML document.
The Prologue
The prologue declares the document as an XML document, specifies the version of XML and the encoding.
<?xml version="1.0" encoding="UTF-8"?>
That's it. You don't have to fool around with the header often.
The Content
Next comes the content, or the payload of the document, typically made of a tree containing elements, attributes, and text.
<somerootelement>
<somechildelement someattribute="attribute">
SomeText
</somechildelement>
</somerootelement>
Creating a C# Class from an XML Document
First off, Visual Studio has a pretty good XML to Class parser that will take an existing XML document and turn it into a class automagically. Unfortunately, if your sample isn't complete, has some edge cases, or you want to do something a little extra fancy, the class won't handle all of that.
Let's use the sample give from the StackOverflow post we talked about from earlier, which contains a list of product elements that each have a "Name" attribute and "Description", "Price", and "Stock" elements:
<Products>
<Product Name="Prod1">
<Description>Desc1</Description >
<Price>100</Price >
<Stock>200</Stock>
</Product>
<Product Name="Prod2">
<Description>Desc2</Description >
<Price>50</Price >
<Stock>400</Stock>
</Product>
</Products>
We would set our classes up as so:
public class Products
{
[XmlElement("Products", Type = typeof(Product))]
public List<Product> Products { get; set; }
}
public class Product
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlElement("Description")]
public string Description { get; set; }
[XmlElement("Price")]
public decimal Price { get; set; }
[XmlElement("Stock")]
public int Stock { get; set; }
}
XML Class Attributes
You'll notice the Attributes in Product
. There are several useful Attributes to set up:
[XmlElement]
By default, an XML element name is determined by the class or member name. - Controlling XML Serialization Using Attributes
By specifying the name explicitly, developers are free to change the name of the member without fear of b0rking the serialization process.
Additionally, the type parameter allows us to point the serializer at a complex type, which has it's own serialization logic
[XmlAttribute]
As with XML elements, the Attribute name parameter allows the class member names to change without affecting serialization.
[XmlIgnore]
XmlIgnore attribute is great for including calculated data that isn't part of the serialization process. For example, let's say we have deserialized the "Product" class and want to add a calculated property for checking if the item is in stock.
public class Product
{
// ...
[XmlElement("Stock")]
public int Stock { get; set; }
[XmlIgnore]
public bool IsInStock => Stock > 0;
}
Now the serializer will ignore this property on serialization and deserialization
Other [Xml] Attributes
Other Xml property attributes can be found at Attributes That Control XML Serialization.
Serialization to XML
Microsoft has done much of the heavy lifting for us:
using System.Xml
using System.Xml.Serialization
public class MySerializer
{
public string ClassToXmlString(MyClass myClass)
{
// saftey first!
if (myClass == null) throw new ArgumentNullException(nameof(myClass));
// initialize components for serialization
var serializer = new XmlSerializer(typeof(MyClass));
var memorystream = new MemoryStream();
var streamwriter = new StreamWriter(memorystream, Encoding.UTF8);
// do the job
serializer.Serialize(streamwriter, myClass);
// read the serialized XML into a string
var streamreader = new StreamReader(memorystream, Encoding.UTF8);
memorystream.Position = 0;
return streamreader.ReadToEnd();
}
}
Deserialization
Stuffing XML back into a class is the easiest thing to do
public MyClass XmlStringToClass(string xml)
{
var serializer = new XmlSerializer(typeof(MyClass));
var reader = new XmlTextReader(new StringReader(xml));
return (MyClass)serializer.Deserialize(reader);
}
Easy as pie.The real key to keeping the whole process from becoming brittle is to decorate your class with attributes so the XmlSerializer can interpret the results regardless of code refactoring. It may take a little longer to set up, but it's certainly worth the time.
Further Reading
- Controlling XML Serialization Using Attribute - Microsoft Developer Network
- Troubleshooting Common Problems with the XmlSerializer - Microsoft Developer Network
- How can I read an XML file properly into a collection when the XML has a specific root element name? - Stack Overflow
- XML Tutorial - W3Schools
- Generating Data Type Classes from XML - Microsoft Developer Network
- Attributes That Control XML Serialization - Microsoft Developer Network
Software Development Nerd