How to convert XML to JSON in
ASP.NET
JSON is a lightweight data-interchange
format. It is particularly useful because it can be 'decoded' easily by web
page JavaScript into object form.AJAX-based web pages use XmlHttpRequest to receive data from a server in response to a user action. While the returned data is normally in XML format, it can also be returned in JSON string format and processed more easily in JavaScript.
Many applications may store information in XML format. However they may want to send data to a client using JSON. To achieve this, they must convert their XML data into JSON format. The ASP.NET C# code below does this job.
Code
Description
The code provides a method private static string XmlToJSON(XmlDocument
xmlDoc) that converts an XmlDocument into a JSON string. The code
iterates through each XML element, its attributes and children, creating the
corresponding JSON objects.
- The code never generates number or boolean values.
- The XML documentElement is always reported as member:object even if it could be shortened by the following rules.
- Element attributes are converted into member "attr_name":"attr_value".
XML
|
JSON
|
<xx yy='nn'></xx>
|
{ "xx": {"yy":"nn"} }
|
<xx yy=''></xx>
|
{ "xx": {"yy":""} }
|
- Element children with no attributes, children or text are converted into member "child_name":null
XML
|
JSON
|
<xx/>
|
{ "xx":null }
|
- Element children with no attributes or children but contain text are converted into "child_name":"child_text"
XML
|
JSON
|
<xx>yyy</xx>
|
{ "xx":"yyy" }
|
- Other element attributes and children are converted into "child_name":object or an array "child_name":[elements] as appropriate, with element text converted into a member with name "value"
XML
|
JSON
|
<xx yy='nn'><mm>zzz</mm></xx>
|
{ "xx": {"yy":"nn",
"mm":"zzz"} }
|
<xx
yy='nn'><mm>zzz</mm><mm>aaa</mm></xx>
|
{ "xx": {"yy":"nn",
"mm": [ "zzz", "aaa" ] } }
|
<xx><mm>zzz</mm>some text</xx>
|
{ "xx": {"mm":"zzz",
"value":"some text"} }
|
<xx value='yyy'>some text<mm>zzz</mm>more
text</xx>
|
{ "xx": {"mm":"zzz",
"value": [ "yyy", "some text", "more
text" ] } }
|
- Characters are made safe for conversion into JSON. Note that this does not protect your JavaScript from attack if any of the source XML comes from an unsafe source, eg user input.
XML
|
JSON
|
<aa>/z'z"z\yyy<aa><
|
{"aa": "\/z\u0027z\"z\\yyy" }
|
In some special circumstances, such as in the example below,
you may need to escape the backslash characters again, eg:
string JSON =
XmlToJSON(doc);
JSON = JSON.Replace(@"\", @"\\");
JSON = JSON.Replace(@"\", @"\\");
Note that there may be security implications for
web pages using unchecked XML contents.
Example
The examples on this page come from my Space Browse site.
XML Input:
<space name="Cake Collage">
<frame>
<photo
img="cakecollage1.jpg" />
<text
string="Browse my cake space" />
<rule
type="F" img="cakecollage9.jpg" x="150"
y="0" w="300" h="250" />
<rule
type="F" img="cakecollage2.jpg" x="0"
y="0" w="150" h="220" />
</frame>
<frame>
<photo
img="cakecollage2.jpg" />
<rule
type="B" img="cakecollage1.jpg" />
<rule
type="L" img="cakecollage3.jpg" />
</frame>
</space>
JSON Output (re-formatted):
{ "space":
{
"name": "Cake Collage",
"frame": [ {"photo": { "img":
"cakecollage1.jpg" },
"rule": [ { "type": "F",
"img":
"cakecollage9.jpg",
"x":
"150",
"y":
"0",
"w":
"300",
"h":
"250"
},
{ "type":
"F",
"img":
"cakecollage2.jpg",
"x":
"0",
"y":
"0",
"w":
"150",
"h":
"220"
}
],
"text": { "string": "Browse my cake space"
}
},
{"photo": { "img": "cakecollage2.jpg" },
"rule": [ { "type": "B", "img":
"cakecollage1.jpg" },
{ "type":
"L", "img":
"cakecollage3.jpg" }
]
}
]
}
}
Once the JSON has been converted into a
JavaScript object, eg called space_DOM, the
following objects are available:
- space_DOM.space.name
- space_DOM.space.frame.length
- space_DOM.space.frame[0].text.string
- space_DOM.space.frame[0].rule[0].type
Your JavaScript code should be flexible to cope
with members not existing, members existing as a single value, or members
existing as an array. I find it useful to have a JavaScript function ObjectToArray which converts all these cases into an Array of length
0, 1 or greater.
function ObjectToArray( obj)
{
if(
!obj) return new Array();
if(
!obj.length) return new Array(obj);
return
obj;
}
space_DOM.space.frame =
ObjectToArray(space_DOM.space.frame);
XmlToJSON
C# code
You may wish to use some of the updates suggsted in the comments
below.
private static string XmlToJSON(XmlDocument
xmlDoc)
{
StringBuilder sbJSON = new StringBuilder();
sbJSON.Append("{
");
XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true);
sbJSON.Append("}");
return
sbJSON.ToString();
}
//
XmlToJSONnode: Output an
XmlElement, possibly as part of a higher array
private static void XmlToJSONnode(StringBuilder
sbJSON, XmlElement node, bool showNodeName)
{
if
(showNodeName)
sbJSON.Append("\"" + SafeJSON(node.Name) + "\":
");
sbJSON.Append("{");
//
Build a sorted list of key-value pairs
// where
key is case-sensitive nodeName
// value is an ArrayList of string or
XmlElement
// so that we know whether the nodeName is an
array or not.
SortedList childNodeNames = new SortedList();
// Add in all node attributes
if(
node.Attributes!=null)
foreach (XmlAttribute attr in node.Attributes)
StoreChildNode(childNodeNames,attr.Name,attr.InnerText);
// Add in all nodes
foreach
(XmlNode cnode in node.ChildNodes)
{
if
(cnode is XmlText)
StoreChildNode(childNodeNames, "value", cnode.InnerText);
else if (cnode is XmlElement)
StoreChildNode(childNodeNames, cnode.Name, cnode);
}
// Now
output all stored info
foreach
(string childname in childNodeNames.Keys)
{
ArrayList
alChild = (ArrayList)childNodeNames[childname];
if
(alChild.Count == 1)
OutputNode(childname, alChild[0], sbJSON, true);
else
{
sbJSON.Append(" \"" + SafeJSON(childname) +
"\": [ ");
foreach (object Child in alChild)
OutputNode(childname, Child, sbJSON, false);
sbJSON.Remove(sbJSON.Length - 2, 2);
sbJSON.Append(" ], ");
}
}
sbJSON.Remove(sbJSON.Length - 2, 2);
sbJSON.Append(" }");
}
//
StoreChildNode: Store data associated with each nodeName
// so that we know whether the
nodeName is an array or not.
private static void StoreChildNode(SortedList
childNodeNames, string nodeName, object nodeValue)
{
//
Pre-process contraction of XmlElement-s
if
(nodeValue is XmlElement)
{
//
Convert <aa></aa> into
"aa":null
//
<aa>xx</aa> into "aa":"xx"
XmlNode cnode = (XmlNode)nodeValue;
if(
cnode.Attributes.Count == 0)
{
XmlNodeList children = cnode.ChildNodes;
if( children.Count==0)
nodeValue = null;
else if (children.Count == 1 && (children[0] is XmlText))
nodeValue = ((XmlText)(children[0])).InnerText;
}
}
// Add
nodeValue to ArrayList associated with each nodeName
// If
nodeName doesn't exist then add it
object
oValuesAL = childNodeNames[nodeName];
ArrayList ValuesAL;
if
(oValuesAL == null)
{
ValuesAL = new ArrayList();
childNodeNames[nodeName] = ValuesAL;
}
else
ValuesAL = (ArrayList)oValuesAL;
ValuesAL.Add(nodeValue);
}
private static void OutputNode(string childname,
object alChild, StringBuilder sbJSON, bool showNodeName)
{
if
(alChild == null)
{
if
(showNodeName)
sbJSON.Append("\"" + SafeJSON(childname) + "\":
");
sbJSON.Append("null");
}
else if
(alChild is string)
{
if
(showNodeName)
sbJSON.Append("\"" + SafeJSON(childname) + "\":
");
string sChild = (string)alChild;
sChild = sChild.Trim();
sbJSON.Append("\"" + SafeJSON(sChild) +
"\"");
}
else
XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName);
sbJSON.Append(", ");
}
// Make a string safe for JSON
private static string SafeJSON(string sIn)
{
StringBuilder sbOut = new StringBuilder(sIn.Length);
foreach
(char ch in sIn)
{
if
(Char.IsControl(ch) || ch == '\'')
{
int ich = (int)ch;
sbOut.Append(@"\u" + ich.ToString("x4"));
continue;
}
else if (ch == '\"' || ch == '\\' || ch == '/')
{
sbOut.Append('\\');
}
sbOut.Append(ch);
}
return
sbOut.ToString();
}
The following code shows how to use XmlToJSON()
when an ASP.NET 2 page loads. It then uses the ASP.NET2 ClientScriptManager to
insert code containing the JSON string into the web page. See the following
section for an example of JavaScript space_processJSON().
protected void Page_Load(object sender, EventArgs
e)
{
XmlDocument doc = new XmlDocument();
try
{
string path = Server.MapPath(".");
doc.Load(path+"whatever.xml");
}
catch
(Exception ex)
{
lblError.Text = ex.ToString();
return;
}
//
Convert XML to a JSON string
string
JSON = XmlToJSON(doc);
//
Replace \ with \\ because string is being decoded twice
JSON =
JSON.Replace(@"\", @"\\");
//
Insert code to process JSON at end of page
ClientScriptManager cs = Page.ClientScript;
cs.RegisterStartupScript(GetType(), "SpaceJSON",
"space_processJSON('" + JSON + "');", true);
}
Client-side
code
<script src="space/json.js"
type="text/javascript"></script>
<script type="text/javascript">
function space_processJSON( JSON)
{
space_DOM = JSON.parseJSON();
if(
!space_DOM)
{
alert("JSON decode error");
return;
}
space_DOM.space.frame
= ObjectToArray(space_DOM.space.frame);
space_frameCount = space_DOM.space.frame.length;
.. or
whatever
}
</script>