How to Convert Xml to JSON Using C#/Linq

How to convert XML to JSON using C#/LINQ?

using System;
using System.Linq;
using System.Web.Script.Serialization;
using System.Xml.Linq;

class Program
{
static void Main()
{
var xml =
@"<Columns>
<Column Name=""key1"" DataType=""Boolean"">True</Column>
<Column Name=""key2"" DataType=""String"">Hello World</Column>
<Column Name=""key3"" DataType=""Integer"">999</Column>
</Columns>";
var dic = XDocument
.Parse(xml)
.Descendants("Column")
.ToDictionary(
c => c.Attribute("Name").Value,
c => c.Value
);
var json = new JavaScriptSerializer().Serialize(dic);
Console.WriteLine(json);
}
}

produces:

{"key1":"True","key2":"Hello World","key3":"999"}

Obviously this treats all the values as strings. If you want to keep the underlying type semantics you could do the following:

using System;
using System.Linq;
using System.Web.Script.Serialization;
using System.Xml.Linq;

class Program
{
static void Main()
{
var xml =
@"<Columns>
<Column Name=""key1"" DataType=""System.Boolean"">True</Column>
<Column Name=""key2"" DataType=""System.String"">Hello World</Column>
<Column Name=""key3"" DataType=""System.Int32"">999</Column>
</Columns>";
var dic = XDocument
.Parse(xml)
.Descendants("Column")
.ToDictionary(
c => c.Attribute("Name").Value,
c => Convert.ChangeType(
c.Value,
typeof(string).Assembly.GetType(c.Attribute("DataType").Value, true)
)
);
var json = new JavaScriptSerializer().Serialize(dic);
Console.WriteLine(json);
}
}

produces:

{"key1":true,"key2":"Hello World","key3":999}

And if you cannot modify the underlying XML structure you will need a custom function that will convert between your custom types and the underlying .NET type:

using System;
using System.Linq;
using System.Web.Script.Serialization;
using System.Xml.Linq;

class Program
{
static void Main()
{
var xml =
@"<Columns>
<Column Name=""key1"" DataType=""Boolean"">True</Column>
<Column Name=""key2"" DataType=""String"">Hello World</Column>
<Column Name=""key3"" DataType=""Integer"">999</Column>
</Columns>";
var dic = XDocument
.Parse(xml)
.Descendants("Column")
.ToDictionary(
c => c.Attribute("Name").Value,
c => Convert.ChangeType(
c.Value,
GetType(c.Attribute("DataType").Value)
)
);
var json = new JavaScriptSerializer().Serialize(dic);
Console.WriteLine(json);
}

private static Type GetType(string type)
{
switch (type)
{
case "Integer":
return typeof(int);
case "String":
return typeof(string);
case "Boolean":
return typeof(bool);
// TODO: add any other types that you want to support
default:
throw new NotSupportedException(
string.Format("The type {0} is not supported", type)
);
}
}
}

How to convert JSON to XML or XML to JSON?

Yes. Using the JsonConvert class which contains helper methods for this precise purpose:

// To convert an XML node contained in string xml into a JSON string   
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc);

// To convert JSON text contained in string json into an XML node
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);

Documentation here: Converting between JSON and XML with Json.NET

Convert XML to JSON and then deserialize to object

The easiest way to deserialize your XML is to do so in one step using XmlSerializer. You can do this by marking your List<T> properties with [XmlElement] to indicate they should be serialized as a collection of repeated elements without an outer container element:

public class ProductionOrderFile
{
public string ProductionOrderName { get; set; }
public string ProductCode { get; set; }
[System.Xml.Serialization.XmlElement]
public List<Batch> Batches { get; set; }
[System.Xml.Serialization.XmlElement]
public List<AggregationLevelConfiguration> Levels { get; set; }
[System.Xml.Serialization.XmlElement]
public List<VariableData> VariableData { get; set; }
}

And then do:

private ProductionOrderFile ParseProductionOrderFile(Stream inputStream)
{
var serializer = new XmlSerializer(typeof(ProductionOrderFile));
return (ProductionOrderFile)serializer.Deserialize(inputStream);
}

Sample fiddle #1.

That being said, if you insist on a two-step deserialization process using an intermediate XmlDocument, you need to modify your code as follows:

  1. Omit the root element by using JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None, true) when creating your intermediate jsonText.

    This removes the root "ProductionOrderFile": { ... } property that does not appear in ProductionOrderFile and bubbles the nested properties up to the top level.

  2. Force the <Batches>, <Levels> and <VariableData> element(s) to be converted to JSON as arrays by following the instructions from Convert XML to JSON and force array.

Thus your code becomes:

private ProductionOrderFile ParseProductionOrderFile(Stream inputStream)
{
// var serializer = new JsonSerializer(); Not needed
XmlDocument doc = new XmlDocument();
doc.Load(inputStream);

foreach (var xPath in new [] { "//Batches", "//Levels", "//VariableData" })
{
foreach (var node in doc.SelectNodes(xPath).Cast<XmlElement>())
{
node.SetAttribute("xmlns:json", "http://james.newtonking.com/projects/json");
node.SetAttribute("Array", "http://james.newtonking.com/projects/json", XmlConvert.ToString(true));
}
}

string jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None, true);

return JsonConvert.DeserializeObject<ProductionOrderFile>(jsonText);
}

Sample fiddle #2.

XDocument or XmlDocument to JSON with C#

Use the method call

JsonConvert.SerializeXNode(x, Formatting.None, true);

this will omit the root node and should create what you expect.

NewtonSoft from XML to JSON

You will have to add an intermediate stage between the XML deserialization and the Json Serialization. When you deserialize the XML to the class, create some new classes that will output to the JSON format you like, then implement some logic to create the JSON classes from the XML classes.

Converting XML to JSON with complex XML data

Using Linq to XML

XDocument xml = //...

var result = xml.Elements("job")
.Select(job => job
.Elements("properties")
.ToDictionary(p => p.Element("name").Value, p => p.Element("value").Value)
);

result will contain a collection of dictionaries, that when serialized,

string json = JsonConvert.SerializeObject(result);

using something like Json.Net, will produce the desired result.

The above example does not include any validation checks, but that can be easily added. The example was just to show how the data can be transformed into the desired model.

Convert XML to Json Array when only one object

Read this documentation about Serialize Xml Node

You can force JSON Array this way

var xml = @"<Items xmlns:json='http://james.newtonking.com/projects/json' >
<Item json:Array='true'>
<Name>name</Name>
<Detail>detail</Detail>
</Item>
</Items>";

DEMO

Convert from HUGE Xml to Json

Try the recommended Microsoft technique:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents

So, for example, you have the following part of code:

                while (reader.Read())  
{
if (reader.NodeType == XmlNodeType.EndElement)
break;
if (reader.NodeType == XmlNodeType.Element
&& reader.Name == "Item")
{
item = XElement.ReadFrom(reader) as XElement;
if (item != null)
{
//here you can get data from your array object
//and put it to your JSON stream
}
}
}

If you want to define the type of element you can check if it has children:
How to check if XElement has any child nodes?

It should work good in pair with streaming of JSON. For more info about JSON steaming look into: Writing JSON to a stream without buffering the string in memory



Related Topics



Leave a reply



Submit