Hi, I'm Ray

I'm a software developer, full-time nerd and occasional human (while heavily caffeinated).

C#: Serialise object to XML (three flavours)

I feel like at some point in every project you find yourself needing to serialise an object. My immediate go-to is JSON: whether it’s being send to another system or saved to disk, the magicians at Newtonsoft have made it super-easy with JSON. However, when I had a requirement to use XML (because I’d be damned to use CSV), the results looked somewhat messier than I expected.

Here’s what I discovered when serialising an object to XML, in three flavours.

Let’s get started.
 

0. Setup

This is the demo class I’m using for testing.


using Newtonsoft.Json;

public class ClassA
{
  [JsonProperty("Prop1")]
  public int Prop1 { get; set; }

  [JsonProperty("Prop2")]
  public string Prop2 { get; set; }

  [JsonProperty("Prop3")]
  public int? Prop3 { get; set; }

  [JsonProperty("Prop4")]
  public bool Prop4 { get; set; }
}

ClassA ob = new ClassA() {
  Prop1 = 200,
  Prop2 = "hello world",
  Prop3 = null,
  Prop4 = true
};

 

1. Vanilla (aka XmlSerializer)

This is the textbook answer using XmlSerializer.


protected string serialiseToXML(Type type, object ob)
{
  StringWriter sw = new StringWriter();

  XmlSerializer xsz = new XmlSerializer(type);
  xsz.Serialize(sw, ob);

  return sw.ToString();
}

The result is good but has lengthy namespace attributes.


<?xml version="1.0" encoding="utf-16"?>
<ClassA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Prop1>200</Prop1>
  <Prop2>hello world</Prop2>
  <Prop3 xsi:nil="true" />
  <Prop4>true</Prop4>
</ClassA>

 

2. Banana (aka XmlSerializer with Namespace)

This is what you find all over the Web when you want to hide the xmlns.


protected string serialiseToXMLNS(Type type, object ob)
{
  StringWriter sw = new StringWriter();

  XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
  ns.Add("", "");

  XmlSerializer xsz = new XmlSerializer(type);
  xsz.Serialize(sw, ob, ns);

  return sw.ToString();
}

The result is good but I still see namespaces.


<?xml version="1.0" encoding="utf-16"?>
<ClassA>
  <Prop1>200</Prop1>
  <Prop2>hello world</Prop2>
  <Prop3 d2p1:nil="true" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance" />
  <Prop4>true</Prop4>
</ClassA>

 

3. Chocolate (aka JsonConvert)

This is a solution some idiot came up with by serialising the object to JSON and then to XML. There’s an added step because XML expects a single root node.


protected string serialiseToXMLByJSON(Type type, object ob)
{
  object newOb = new { root = ob };
  string rv = JsonConvert.SerializeObject(newOb);

  XmlDocument doc = JsonConvert.DeserializeXmlNode(rv);
  return string.Concat("<", type.Name , ">", doc.FirstChild.InnerXml, "</", type.Name, ">");
}

The result is minimised.


<ClassA><Prop1>200</Prop1><Prop2>hello world</Prop2><Prop3 /><Prop4>true</Prop4></ClassA>

 

A. Deserialise for (1) and (2)

I didn’t want to show code for serialising an object to XML without including code to deserialise back to the object.


StringReader sr = new StringReader(xml);
XmlSerializer xsz = new XmlSerializer(typeof(ClassA));
ClassA ob = (ClassA)xsz.Deserialize(sr);

 

B. Deserialise for (3)

Remember that root node that XML needs? Yes, we have to handle that when deserialising the XML to JSON then to object.


protected ClassA deserialiseXMLJSONObject(string xml)
{
  XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);

  // remove root node
  string json = JsonConvert.SerializeXmlNode(doc.FirstChild);
  int n = json.IndexOf(":") + 1;

  json = json.Substring(n, (json.Length - n - 1));

  return JsonConvert.DeserializeObject<ClassA>(json);
}

 

I hope someone finds this useful.

The Author

Ray
  • Hi, I'm Ray. I'm a software developer, full-time nerd and occasional human (while heavily caffeinated).

Copyright © 2014-2019 Ray Lam. All Rights Reserved.