In this article, we are going to learn how to convert JSON to XML and XML to JSON in C#.
We are going to explore how we can do such conversions using the popular Newtonsoft.Json
library and the native System.Text.Json library.
Newtonsoft.Json vs Native JSON Library
Before starting, it’s important to know that the native System.Text.Json
library does not offer any direct way to do conversion between JSON and XML. All we have are some workaround ways that are limited to well-defined JSON/XML data structures. Newtonsoft.Json
on the other hand, is quite convenient by allowing us to directly do such conversions along with a bunch of tailoring options.
So, let’s first explore the Newtonsoft-ways of such conversions.
Convert XML to JSON Using Newtonsoft.Json
To begin with, we are going to add a sample XML data source to our class library project:
public class MovieStats { public static string Xml => @"<?xml version='1.0'?> <SquidGame> <Genre>Thriller</Genre> <Rating Type='Imdb'>8.1</Rating> <Stars>Lee Jung-jae</Stars> <Stars>Park Hae-soo</Stars> <Budget /> </SquidGame>"; }
We declare a MovieStats
container class for our sample data sources. For now, it consists of an Xml
property that holds information about the SquidGame movie in XML format. We aim to transform this into a JSON string.
Basic Conversion
Next, let’s add a JsonXmlUtils
utility class to hold our conversion routines using Newtonsoft library:
public static class JsonXmlUtils { public static string XmlToJson(string xml) { var doc = XDocument.Parse(xml); return JsonConvert.SerializeXNode(doc); } }
Straightaway, we come up with an XmlToJson
helper method. Inside this method, we first form an XDocument
object by parsing the supplied XML string. Then we call the JsonConvert.SerializeXNode
method that turns XDocument
into a JSON string. As simple as that.
Applying this routine to our sample XML:
var json = JsonXmlUtils.XmlToJson(MovieStats.Xml);
Results in a minimal JSON output:
{"?xml":{"@version":"1.0"},"SquidGame":{"Genre":"Thriller","Rating":{"@Type":"Imdb","#text":"8.1"},"Stars":["Lee Jung-jae","Park Hae-soo"],"Budget":null}}
The resulting string contains all the information from the XML in a standard JSON format.
A similar approach is applicable using classic XML DOM i.e. XmlDocument
:
public static string XmlDocumentToJson(string xml) { var doc = new XmlDocument(); doc.LoadXml(xml); return JsonConvert.SerializeXmlNode(doc); }
Here, we instantiate an XmlDocument
instance and load the XML string into it. After that, we invoke the JsonConvert.SerializeXmlNode
method, which is just an equivalent of the XDocument
version. The resulting JSON is the same as the previous one.
Better Readability
So, we have seen a minimalistic approach for producing JSON output. However, this output lacks readability. We can prettify this using another overload of SerializeXNode
that takes a Formatting
option:
public static string XmlToPrettyJson(string xml) { var doc = XDocument.Parse(xml); return JsonConvert.SerializeXNode(doc, Formatting.Indented); }
After we apply this method to the same XML sample, it will produce a nicely indented output:
{ "?xml": { "@version": "1.0" }, "SquidGame": { "Genre": "Thriller", "Rating": { "@Type": "Imdb", "#text": "8.1" }, "Stars": [ "Lee Jung-jae", "Park Hae-soo" ], "Budget": null } }
We now have a greatly readable JSON.
However, we should be cautious about using pretty printing as it comes at the price of performance and bandwidth.
XML to JSON Conversion Rules
A closer look at the output reveals a few XML to JSON conversion facts:
- XML declaration (
<?xml … ?>
) and other processing instructions keep their place in JSON output with a “?” prefix - Attributes are prefixed with an “@” e.g. “@version“, “@Type“, etc
- A text node with attribute(s) transforms to an associated JSON dictionary with the content represents by the “#text” key
- Repeating elements in the same level combine into an array like the Stars array in our JSON for example
- Empty elements transform to null e.g. the Budget element
- Numbers will turn to strings as we can see for the Rating element
Omission of Root Object
One of the provisions of XML data structure is the need for a single root element. Sometimes this forces the applications to wrap their business data into a dummy root element. This sort of XML data needs unwrapping while transforming to JSON to reflect the originally intended data.
Fortunately, Newtonsoft supports this unwrapping out of the box:
public static string XmlToJsonWithoutRoot(string xml) { var doc = XDocument.Parse(xml); return JsonConvert.SerializeXNode(doc, Formatting.None, omitRootObject: true); }
As elegant as it looks! We just need to call an overload of SerializeXNode
that accepts the omitRootObject
parameter.
When we try this routine on a sample XML:
var xml = @" <SquidGame> <Name>Squid Game</Name> <Genre>Thriller</Genre> </SquidGame>"; var json = JsonXmlUtils.XmlToJsonWithoutRoot(xml);
We get an unwrapped JSON structure:
{"Name":"Squid Game","Genre":"Thriller"}
No more worries about dummy roots!
There is one caveat though. The omission of the root object does not work well when we have XML declaration in place and results in an invalid JSON.
Conversion of Repeating XML Elements to JSON Array
Talking about the repeating XML elements, they need special attention for the sake of consistency. Since the repetition can occur once or multiple times, different XML content may result in different JSON structures, even though the underlying XML intends to represent one consistent structure.
For example, if we omit one of the Stars elements from our sample XML and apply the JSON conversion:
var xml = @"<?xml version='1.0'?> <SquidGame> <Genre>Thriller</Genre> <Rating Type='Imdb'>8.1</Rating> <Stars>Lee Jung-jae</Stars> <Budget /> </SquidGame>"; var json = JsonXmlUtils.XmlToJson(xml);
We will end up with a plain JSON string for Stars instead of an array:
{ "?xml": { "@version": "1.0" }, "SquidGame": { "Genre": "Thriller", "Rating": { "@Type": "Imdb", "#text": "8.1" }, "Stars": "Lee Jung-jae", "Budget": null } }
This is problematic when the consumer always expects an array of Stars.
A quick remedy is possible if we can manipulate the incoming XML string during its generation:
var xml = @"<?xml version='1.0'?> <SquidGame xmlns:json='http://james.newtonking.com/projects/json'> <Genre>Thriller</Genre> <Rating Type='Imdb'>8.1</Rating> <Stars json:Array='true'>Lee Jung-jae</Stars> <Budget /> </SquidGame>"; var json = JsonXmlUtils.XmlToJson(xml);
The highlighted lines are all we need to change in the XML. We add a namespace for Newtonsoft’s JSON schema in the root element. This allows us to set a flag attribute json:Array
to true
on the target element (Stars tag in our case). During serialization this attribute instructs the converter to forcefully interpret it as a JSON array:
{ "?xml": { "@version": "1.0" }, "SquidGame": { "Genre": "Thriller", "Rating": { "@Type": "Imdb", "#text": "8.1" }, "Stars": ["Lee Jung-jae"], "Budget": null } }
As we expect, the JSON result now holds Stars in an array.
Obviously, this solution is not feasible for external XML. In that case, we can programmatically do such manipulations during the formation of XDocument
:
public static string XmlToJsonWithJsonArrayFlag(string xml) { var doc = XDocument.Parse(xml); if (doc.Root!.Elements("Stars") is { } elements && elements.Count() == 1) { var jsonNamespace = "http://james.newtonking.com/projects/json"; doc.Root!.Add(new XAttribute(XNamespace.Xmlns + "json", jsonNamespace)); elements.Single().Add(new XAttribute(XNamespace.Get(jsonNamespace) + "Array", true)); } return JsonConvert.SerializeXNode(doc); }
We create the XDocument
as usual. Then comes the DOM customization part that we achieve using LinqToXml
API.
As part of it, we first check whether the repetition of “Stars” occurs only a single time. if not, we don’t need any change. Next to it, we declare the necessary JSON namespace and add it to the Root
element. Finally, we mark the target element with the json:Array
flag. That’s it.
The output is no different than the previous one.
Convert JSON to XML Using Newtonsoft.Json
So far we have seen various methods for XML to JSON conversion. Newtonsoft.Json
also provides similar equivalent methods that do the reverse conversion.
So, it’s time to add a JSON sample in our MovieStats
class:
public static string Json => @" { ""SquidGame"": { ""Genre"": ""Thriller"", ""Rating"": { ""@Type"": ""Imdb"", ""#text"": ""8.1"", }, ""Stars"": [""Lee Jung-jae"", ""Park Hae-soo""], ""Budget"": null } }";
This is nothing but an equivalent JSON of the previous XML sample.
XML Conformity Factors
Before going for the conversion routines, let’s take a look at the XML conformity factors that we need to be aware of:
XML must have a single root element – Which means we either need a top-level JSON node or ensure that the resulting XML has a dummy one.
Element name can’t have whitespace – If JSON has any such property name, the whitespaces will turn to their URL-encoded representation i.e. “_x0020_”.
Element name can’t start with a digit – Similar to whitespace it will turn to its URL-encoded value.
Basic Conversion
With all those provisions in mind, we’re ready for the basic JSON to XML conversion routine in the Newtonsoft/JsonXmlUtils
class:
private static readonly XDeclaration _defaultDeclaration = new("1.0", null, null); public static string JsonToXml(string json) { var doc = JsonConvert.DeserializeXNode(json)!; var declaration = doc.Declaration ?? _defaultDeclaration; return $"{declaration}{Environment.NewLine}{doc}"; }
Once again, we get a conversion function in a few simple steps. This time we call the DeserializeXNode
method of JsonConvert
turning the JSON string into an XDocument
.
After that, we prepare an XML declaration. If the supplied JSON includes a declaration part we use that, otherwise we use a default declaration which is nothing but <?xml version="1.0"?>
. Finally, we merge the declaration part and the XML body. This way, we ensure a well-formed XML output.
A call to this function with the MovieStats.Json
:
var json = MovieStats.Json; var xml = JsonXmlUtils.JsonToXml(json);
Generates an XML string:
<?xml version="1.0"?> <SquidGame> <Genre>Thriller</Genre> <Rating Type="Imdb">8.1</Rating> <Stars>Lee Jung-jae</Stars> <Stars>Park Hae-soo</Stars> <Budget /> </SquidGame>
As we expect, this output is the same XML that we previously used for JSON generation.
Conversion of JSON without Top-level Node
The previous example uses a JSON with a single top-level “SquidGame” node and all other fields inside it.
Let’s see what happens if we don’t have a root object in the JSON graph:
var json = @"{""Name"":""Squid Game"",""Genre"":""Thriller""}"; Assert.ThrowsAny<JsonSerializationException>(() => JsonXmlUtils.JsonToXml(json));
Yes, our basic routine fails to convert such JSON. The good thing is that Newtonsoft provides us with a straightforward solution for this:
public static string JsonToXmlWithExplicitRoot(string json, string rootName) { var doc = JsonConvert.DeserializeXNode(json, rootName)!; var declaration = doc.Declaration ?? _defaultDeclaration; return $"{declaration}{Environment.NewLine}{doc}"; }
The only difference in this variant is we use an overload of DeserializeXNode
method that takes a rootName
parameter. This allows us to specify an explicit root element. The converter adds the root object on its own and generates the XML accordingly.
Calling this function on the same JSON sample with a root name of “Movie”:
var json = @"{""Name"":""Squid Game"",""Genre"":""Thriller""}"; var xml = JsonXmlUtils.JsonToXmlWithExplicitRoot(json, "Movie");
Successfully produces the XML content:
<?xml version="1.0"?> <Movie> <Name>Squid Game</Name> <Genre>Thriller</Genre> </Movie>
We get exactly what we desire.
Also, it is completely ok to apply this function on a JSON already having a top-level node. It just wraps the entire XML body inside the new root.
Convert JSON to XML Using System.Text.Json
Undoubtedly, Newtonsoft offers us quite elegant ways to deal with JSON-XML conversions. However, it’s not an uncommon demand to keep away from third-party dependency. So, it’s time to explore the native ways to do such things. As mentioned earlier, we have no direct method for this, just a few workarounds:
Deserialize JSON to an intermediate POCO and serialize back to XML. This is the recommended way to go if the JSON structure is well-known. This strategy also works for reverse transformation from XML to JSON.
Using a custom converter. This needs an in-depth understanding of JSON nodes, data types, and their equivalent XML counterparts as well as handling of valid XML constructs. As it needs everything to build from scratch, it’s not recommended unless it’s the only way to go. This indeed needs a separate post that we will try to cover in a future article.
Preparation of POCO Models
So, our preference is to use POCO as a transition object between JSON and XML. To make this work, in our case, we need CLR models of the MovieStats.Json
sample.
We can prepare equivalent models on our own. Alternatively, we can auto-generate models using smart IDE tools out there and tailor them as we need. For example, the Visual Studio has a nice feature to create POCO models from a JSON string copied in the clipboard. We also need to apply some XML and JSON attributes there to control the serialization/deserialization as we desire.
Check out XML Serialization and JSON Serialization for more details.
In the end, we have several models that reflect our target JSON structure:
public class RootObject { public SquidGame? SquidGame { get; set; } } public class SquidGame { public string? Genre { get; set; } public Rating? Rating { get; set; } [XmlElement] public string[]? Stars { get; set; } public object? Budget { get; set; } } public class Rating { [XmlAttribute] [JsonPropertyName("@Type")] public string? Type { get; set; } [XmlText] [JsonPropertyName("#text")] public string? Text { get; set; } }
With these models in hand, we can now implement the conversion routine.
Conversion Method
As planned, we’re going to implement it in two core steps: JSON to .NET object conversion and object to XML conversion. JsonSerializer
from System.Text.Json
library and XmlSerializer
from System.Xml.Serialization
library are our friends here to achieve this:
public static string JsonToXml(string json) { var obj = JsonSerializer.Deserialize<RootObject>(json)!; return ObjectToXml(obj.SquidGame); } static string ObjectToXml<T>(T obj) { var xmlSerializer = new XmlSerializer(typeof(T)); var sb = new StringBuilder(); using var xmlWriter = XmlWriter.Create(sb); var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); xmlSerializer.Serialize(xmlWriter, obj, ns); return sb.ToString(); }
In the first step, we deserialize the JSON to RootObject
using the Deserialize
method of the JsonSerializer
class. This RootObject
holds our SquidGame
target object as a property. Subsequently, we serialize this to XML using the ObjectToXml
helper method.
Inside the ObjectToXml
method, we first instantiate an XmlSerializer
instance for the target object type. This serializer instance is the key performer which serializes the target object and writes to an XmlWriter
. So, we need an XmlWriter
which we can create by wrapping a StringBuilder
instance. We also use an empty namespace that instructs the serializer to skip default namespace generation and ensure a clean output. Whenever the serializer writes to XmlWriter
, it is eventually appended to the StringBuilder
. Finally, we get the XML content by calling the ToString
method.
As we try this routine with MovieStats.Json
sample:
var xml = JsonXmlUtils.JsonToXml(MovieStats.Json);
We get a well-formed XML output:
<?xml version="1.0" encoding="utf-16"?> <SquidGame> <Genre>Thriller</Genre> <Rating Type="Imdb">8.1</Rating> <Stars>Lee Jung-jae</Stars> <Stars>Park Hae-soo</Stars> </SquidGame>
Convert XML to JSON Using System.Text.Json
We can develop XML to JSON transformation routine in a similar fashion:
public static string XmlToJson(string xml) { var obj = XmlToObject<SquidGame>(xml); return JsonSerializer.Serialize(new RootObject { SquidGame = obj }); } static T XmlToObject<T>(string xml) { var xmlSerializer = new XmlSerializer(typeof(T)); using var stringReader = new StringReader(xml); return (T)xmlSerializer.Deserialize(stringReader)!; }
Again, we develop the method in two core steps.
We first call an XmlToObject
helper method that transforms XML to a SquidGame
object instance. Inside this helper method, we use a StringReader
to read the supplied XML. Then, we Deserialize
this reader object using an XmlSerializer
instance.
We accomplish the final JSON output by using Serialize
method of JsonSerializer
on the deserialized object. Since we want to keep the SquidGame
node in our JSON graph, we have to wrap this within a RootObject
.
This method rightly transforms MovieStats.Xml
:
var json = JsonXmlUtils.XmlToJson(MovieStats.Xml);
To its JSON equivalent:
{ "SquidGame": { "Genre": "Thriller", "Rating": { "@Type": "Imdb", "#text": "8.1" }, "Stars": [ "Lee Jung-jae", "Park Hae-soo" ], "Budget": {} } }
Performance Analysis
Now that we have JSON-XML transformation routines for two different libraries, it’s time for a benchmark analysis.
Once we run the benchmark program, we can inspect the result:
| Method | Categories | Mean | Error | StdDev | Ratio | |-------------------- |------------ |----------:|----------:|----------:|------:| | NewtonsoftJsonToXml | JSON to XML | 6.465 μs | 0.0739 μs | 0.0691 μs | 0.73 | | SystemTextJsonToXml | JSON to XML | 8.898 μs | 0.0986 μs | 0.0874 μs | 1.00 | | | | | | | | | NewtonsoftXmlToJson | XML to JSON | 7.705 μs | 0.0511 μs | 0.0399 μs | 0.51 | | SystemTextXmlToJson | XML to JSON | 15.053 μs | 0.2997 μs | 0.2944 μs | 1.00 |
No surprise, Newtonsoft variants perform significantly faster than their native counterparts.
Conclusion
In this article, we have learned how to convert JSON to XML and XML to JSON in C#. We have explored the native ways, as well as the ways, using Newtonsoft.Json
and our performance analysis shows that the latter one performs much better.
Thank you so much for this helpful article.
Can you also show the 2nd alternative too, i.e., Using a custom converter, for System.Text.Json.
Need it badly. Much appreciated. 🙂
Hello Shaggie. Thank you for reading the article. Using a custom converter is not an easy topic, and if we will cover it, we will probably do it in a separate article (probably multiple ones).