XML parsing using C#, get specific value - c#

I have the following XML:
<XML>
<Properties>
<Property name="ActionLogPrompt">2</Property>
<Property name="Answer"></Property>
<Property name="SubQBackColour">#FF0000</Property>
</Properties>
</XML>
What I am trying to do is to find SubQBackColour and get the value of "#FF0000". I am trying to do that with the code below, but I cannot figure out what to call to get the name of the property and check for this SubQBackColour.
while (reader.Read())
{
node = reader.NodeType;
if (node == XmlNodeType.Element)
{
if (reader.Name.Equals("XML"))
{
reader.Read();
if (reader.Name.Equals("Properties"))
{
reader.Read();
if (reader.Name.Equals("Property"))
{
reader.Read();
node = reader.NodeType;
if ((node == XmlNodeType.Text))
{
string thingy = reader.GetAttribute("name");
}
}
}
}
}
}

Try this...
string sXML = "<XML>"+
"<Properties>"+
"<Property name=\"ActionLogPrompt\">2</Property>"+
"<Property name=\"Answer\"></Property>"+
"<Property name=\"SubQBackColour\">#FF0000</Property>"+
"</Properties></XML>";
XDocument doc3 = XDocument.Parse(sXML);
var v= from p in doc3.Descendants("Property")
where p.Value == "#FF0000"
select p.Attribute("name").Value;
Console.WriteLine(v);
You will get value of "name" attribute as "SubQBackColour". You can change the query to get whatever you want.
The best implementation would be to create a class to hold property name and value and get that as output.
Something like
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
string sXML = "<XML>"+
"<Properties>"+
"<Property name=\"ActionLogPrompt\">2</Property>"+
"<Property name=\"Answer\"></Property>"+
"<Property name=\"SubQBackColour\">#FF0000</Property>"+
"</Properties></XML>";
XDocument doc3 = XDocument.Parse(sXML);
var v4 = from p in doc3.Descendants("Property")
where p.Value == "#FF0000"
select new Property
{
Name = p.Attribute("name").Value,
Value = p.Value
};
Console.WriteLine(v4.FirstOrDefault().Name);

You can try this code
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("c:\\yourxml.xml");
XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes("/Properties");
string actionLogPrompt= "", answer= "", subQBackColour="";
foreach (XmlNode node in nodeList)
{
actionLogPrompt = node.SelectSingleNode("ActionLogPrompt").InnerText;
answer = node.SelectSingleNode("Answer").InnerText;
subQBackColour= node.SelectSingleNode("SubQBackColour").InnerText;
MessageBox.Show(actionLogPrompt+ " " + answer+ " " + subQBackColour);
}

Related

Skip String if null

I am writing a program that calls an API, receives XML and displays the tracking aswell as opening a web browser with tracking info in. I have a problem, if the wrong tracking id is entered the nodes return null and the string will be null, how can I display an error rather than throwing a null ref exception?
//ONLINE PORTAL TRACKING
string ConNo = Con.Text;
XmlDocument doc1 = new XmlDocument();
doc1.Load(url1 + ConNo + url2 + APIKEY);
XmlElement root = doc1.DocumentElement;
XmlNodeList nodes = root.SelectNodes("/Response/Detail/Data");
foreach (XmlNode node in nodes)
{
string ConsignmentNumber = node["ConNo"].InnerText;
string tracking_url = node["tracking_url"].InnerText;
string Lifts = node["Lifts"].InnerText;
string TrackingID = node["TrackingID"].InnerText;
string JobStatus = node["Status"].InnerText;
string Barcodes = node["Barcodes"].InnerText;
string Weight = node["Weight"].InnerText;
string DateMan = node["DateEntered"].InnerText;
this.ConsignmentNumber.Text = ConsignmentNumber;
this.Lifts.Text = Lifts;
// this.TrackingURL.Text = tracking_url;
this.TrackingID.Text = TrackingID;
this.JobStatus.Text = JobStatus;
this.Barcodes.Text = Barcodes;
this.Weight.Text = Weight;
this.DelMan.Text = DateMan;
System.Diagnostics.Process.Start(PortalURL1 + TrackingID);
You can try to get some information in the XML response in case of error. Something like this:
XmlNodeList nodes = root.SelectNodes("/Response/Detail/Data");
if (nodes == null)
{
// Look the root content to see if contains a node with the error.
// In that case, do a SelectSingleNode. Here is an example with a
// sample path (is not real, I don't know anything about the XML
// error response schema)
XmlNode node = root.SelectSingleNode("/Response/Detail/Error");
var error = node?.InnerText;
if (string.IsNullOrEmpty(error))
{
// If for any reason we can't get any error...
error = $"Unknown error on query: {ConNo}";
}
}
else
{
foreach (XmlNode node in nodes)
{
// ...
}
}

How to get value from a specific child element in XML using XmlReader

Here is the XML
<?xml version="1.0" encoding="UTF-8"?>
<Data_Speed>
<Tech ID = "UMTS">
<Coverage ID="Dense_Urban">
<DownLoad_Speed>10</DownLoad_Speed>
<Upload_Speed>20</Upload_Speed>
</Coverage>
<Coverage ID="Urban">
<DownLoad_Speed>30</DownLoad_Speed>
<Upload_Speed>40</Upload_Speed>
</Coverage>
<Coverage ID="SubUrban">
<DownLoad_Speed>50</DownLoad_Speed>
<Upload_Speed>60</Upload_Speed>
</Coverage>
</Tech>
<Tech ID = "UMTS900">
<Coverage ID="Dense_Urban">
<DownLoad_Speed>11</DownLoad_Speed>
<Upload_Speed>12</Upload_Speed>
</Coverage>
<Coverage ID="Urban">
<DownLoad_Speed>13</DownLoad_Speed>
<Upload_Speed>14</Upload_Speed>
</Coverage>
<Coverage ID="SubUrban">
<DownLoad_Speed>15</DownLoad_Speed>
<Upload_Speed>16</Upload_Speed>
</Coverage>
</Tech>
<Tech ID = "4G800">
<Coverage ID="Dense_Urban">
<DownLoad_Speed>30</DownLoad_Speed>
<Upload_Speed>42</Upload_Speed>
</Coverage>
<Coverage ID="Urban">
<DownLoad_Speed>50</DownLoad_Speed>
<Upload_Speed>34</Upload_Speed>
</Coverage>
<Coverage ID="SubUrban">
<DownLoad_Speed>45</DownLoad_Speed>
<Upload_Speed>46</Upload_Speed>
</Coverage>
<Coverage ID="Rural">
<DownLoad_Speed>47</DownLoad_Speed>
<Upload_Speed>48</Upload_Speed>
</Coverage>
<Coverage ID="Variable">
<DownLoad_Speed>15</DownLoad_Speed>
<Upload_Speed>52</Upload_Speed>
</Coverage>
<Coverage ID="Outdoor">
<DownLoad_Speed>25</DownLoad_Speed>
<Upload_Speed>22</Upload_Speed>
</Coverage>
</Tech>
</Data_Speed>
So how could I get value of DownLoad_Speed> & UpLoad_Speed> element by given Coverage ID Urban> and Tech ID UMTS900>? Say, if I give tech id value = "UMTS900" and coverage id value "Urban", I'd like to have string value dwnload_speed = 13 and Upload_Speed = 14 as result.
Using Linq to Xml
XDocument doc = XDocument.Load(filepath);
//ex...
string technology = "UMTS900";
string coverage = "Dense_Urban";
var result = doc.Descendants("Tech")
.Where(x=> (string)x.Attribute("ID") == technology)
.Elements("Coverage")
.Where(x=>(string)x.Attribute("ID")== coverage)
.Select(x=> new
{
Dowload_Speed = (string)x.Element("DownLoad_Speed"),
Upload_Speed = (string)x.Element("Upload_Speed")
});
Check this Demo
You need to open XML document
XmlDocument _document = new XmlDocument();
byte[] bytes = File.ReadAllBytes(filePath);
string xml = Encoding.UTF8.GetString(bytes);
try
{
_document.LoadXml(xml);
}
catch (XmlException e)
{
//exception handling
}
var doc = (XmlDocument)_document.CloneNode(true);
XmlNode node = doc.GetElementsByTagName("your child node name");
Once you get your node then you can do necessary thing with it
string techId = "UMTS900";
string coverageId = "SUBURBAN";
int downloadSpeed = 0;
int uploadSpeed = 0;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"C:\....\DataSpeed.xml");
XmlNodeList techTags = xmlDoc.GetElementsByTagName("Tech");
foreach (XmlNode techTag in techTags)
{
if (techTag.Attributes["ID"].Value.Equals(techId,StringComparison.OrdinalIgnoreCase))
{
XmlNodeList coverageTags = techTag.ChildNodes;
foreach (XmlNode coverageTag in coverageTags)
{
if (coverageTag.Attributes["ID"].Value.Equals(coverageId, StringComparison.OrdinalIgnoreCase))
{
downloadSpeed =Convert.ToInt16(coverageTag.ChildNodes[0].InnerText);
uploadSpeed = Convert.ToInt16(coverageTag.ChildNodes[1].InnerText);
break;
}
}
break;
}
}
if (downloadSpeed == 0 && uploadSpeed == 0)
{
Console.WriteLine("Specified Tech Id and Coverage Id not found");
}
else
{
Console.WriteLine("Download Speed is {0}. Upload Speed is {1}.",downloadSpeed,uploadSpeed);
}
Here is solution using XmlReader with xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication4
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
string id = "UMTS900";
object coverages = null;
while (!reader.EOF)
{
if (reader.Name != "Tech")
{
reader.ReadToFollowing("Tech");
}
if (!reader.EOF)
{
XElement tech = (XElement)XElement.ReadFrom(reader);
if((string)tech.Attribute("ID") == id)
{
coverages = tech.Descendants("Coverage").Select(x => new
{
id = (string)x.Attribute("ID"),
downLoad_Speed = (int)x.Element("DownLoad_Speed"),
upLoad_Speed = (int)x.Element("Upload_Speed"),
}).ToList();
break;
}
}
}
}
}
}

Adding Subroot elements in XML

I'm making an XML Document which contains subroot nodes. I'm using XmlDocument and adding the child nodes.
This is my code:
XmlDocument doc = new XmlDocument();
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
doc.AppendChild(dec);
XmlElement root = doc.CreateElement("LICENSE");
if (strGInfo == "")
{
strGInfo = "N/A";
}
XmlElement ginfo = doc.CreateElement("GENERAL_INFO");
ginfo.InnerText = strGInfo;
root.AppendChild(ginfo);
if (strLNo == "")
{
strLNo = "N/A";
}
XmlElement subroot = doc.CreateElement("LICENSE_INFO");
//XmlElement root1 = doc.CreateElement("LICENSE_INFO");
XmlElement lno = doc.CreateElement("LICENCE_NO");
lno.InnerText = txtLNo.Text;
subroot.AppendChild(lno);
if (strUID == "")
{
strUID = "N/A";
}
XmlElement uid = doc.CreateElement("USER_ID");
uid.InnerText = txtUID.Text;
subroot.AppendChild(uid);
if (strOrg == "")
{
strOrg = "N/A";
}
XmlElement org = doc.CreateElement("ORGANIZATION");
org.InnerText = txtOrg.Text;
subroot.AppendChild(org);
if (strUName == "")
{
strUName = "N/A";
}
XmlElement uname = doc.CreateElement("USER_NAME");
uname.InnerText = txtUName.Text;
subroot.AppendChild(uname);
if (strSType == "")
{
strSType = "N/A";
}
XmlElement stype = doc.CreateElement("SOLUTION_TYPE");
stype.InnerText = txtSType.Text;
subroot.AppendChild(stype);
if (strVer == "")
{
strVer = "N/A";
}
XmlElement ver = doc.CreateElement("VERSION");
ver.InnerText = txtVer.Text;
subroot.AppendChild(ver);
XmlElement ltype = doc.CreateElement("LICENCE_TYPE");
ltype.InnerText = drpLType.SelectedItem.Text;
subroot.AppendChild(ltype);
if (strMeapSupp == "")
{
strMeapSupp = "N/A";
}
XmlElement meapsupp = doc.CreateElement("MEAP_SUPPORT");
meapsupp.InnerText = rdoMeapSupport.Text;
subroot.AppendChild(meapsupp);
XmlElement LicFrom = doc.CreateElement("LICENCE_FROM");
LicFrom.InnerText = lblLFrom.Text;
subroot.AppendChild(LicFrom);
XmlElement LicTo = doc.CreateElement("LICENCE_TO");
LicTo.InnerText = lblLTo.Text;
subroot.AppendChild(LicTo);
XmlElement suppfrom = doc.CreateElement("SUPPORT_FROM");
suppfrom.InnerText = lblSuppFrom.Text;
subroot.AppendChild(suppfrom);
XmlElement suppto = doc.CreateElement("SUPPORT_TO");
suppto.InnerText = lblSuppTo.Text;
subroot.AppendChild(suppto);
doc.AppendChild(subroot);
XmlElement subroot2 = doc.CreateElement("LICENCE_CONSTRAINT");
if (strMaxUsr == "")
{
strMaxUsr = "N/A";
}
XmlElement maxusr = doc.CreateElement("MAX_USER");
maxusr.InnerText = txtMaxUsr.Text;
subroot2.AppendChild(maxusr);
if (strMaxMach == "")
{
strMaxMach = "N/A";
}
XmlElement maxmach = doc.CreateElement("MAX_MACHINE");
maxmach.InnerText = txtMaxMach.Text;
subroot2.AppendChild(maxmach);
if (strMachIP == "")
{
strMachIP = "N/A";
}
doc.AppendChild(subroot2);
XmlElement subroot3 = doc.CreateElement("MACHINE_INFO");
XmlElement machip = doc.CreateElement("MACHINE_IP");
machip.InnerText = txtMachIP.Text;
subroot3.AppendChild(machip);
if (strMachMac == "")
{
strMachMac = "N/A";
}
XmlElement machmac = doc.CreateElement("MACHINE_MAC");
machmac.InnerText = txtMachMac.Text;
subroot3.AppendChild(machmac);
doc.AppendChild(subroot3);
XmlElement subroot4 = doc.CreateElement("LICENCE_SIGNATURE");
XmlElement UqID = doc.CreateElement("UNIQUE_ID");
UqID.InnerText = txtUqID.Text;
subroot4.AppendChild(UqID);
doc.AppendChild(subroot4);
doc.Save(#"D:\New.xml");
My XML Document should look something like this:
-<LICENSE>
<GENERAL_INFO> </GENERAL INFO>
-<LICENSE_INFO>
<LICENSE_NO> </LICENSE_NO>
<USER_ID> </USER_ID> //etc
-<LICENCE_CONSTRAINT>
<MAX_USER> </MAX_USER>
<MAX_MACHINE> </MAX_MACHINE>
</LICENCE_CONSTRAINT>
-<MACHINE_INFO>
<MACHINE_IP> </MACHINE_IP>
<MACHINE_MAC> </MACHINE_MAC>
</MACHINE_INFO>
</LICENSE_INFO>
Where am I going wrong?
I would use Linq2Xml for this
string xml = FormXml(licenceNo: "1",machineIP:"1.2.3.4",generalInfo:"some Info");
public string FormXml(
string generalInfo="N/A",
string licenceNo="N/A",
string userID="N/A",
string maxUser="N/A",
string maxMachine="N/A",
string machineIP="N/A",
string machineMAC="N/A")
{
return new XElement("LICENSE",
new XElement("GENERAL_INFO", generalInfo),
new XElement("LICENSE_INFO",
new XElement("LICENSE_NO", licenceNo),
new XElement("USER_ID", userID)),
new XElement("LICENCE_CONSTRAINT",
new XElement("MAX_USER", maxUser),
new XElement("MAX_MACHINE", maxMachine)),
new XElement("MACHINE_INFO",
new XElement("MACHINE_IP", machineIP),
new XElement("MACHINE_MAC", machineMAC))).ToString();
}
and OUTPUT:
<LICENSE>
<GENERAL_INFO>some Info</GENERAL_INFO>
<LICENSE_INFO>
<LICENSE_NO>1</LICENSE_NO>
<USER_ID>N/A</USER_ID>
</LICENSE_INFO>
<LICENCE_CONSTRAINT>
<MAX_USER>N/A</MAX_USER>
<MAX_MACHINE>N/A</MAX_MACHINE>
</LICENCE_CONSTRAINT>
<MACHINE_INFO>
<MACHINE_IP>1.2.3.4</MACHINE_IP>
<MACHINE_MAC>N/A</MACHINE_MAC>
</MACHINE_INFO>
</LICENSE>
you are creating your root element "LICENSE":
XmlElement root = doc.CreateElement("LICENSE");
but you're not appending it to doc.
Furthermore, you are appending to doc multiple times:
doc.AppendChild(subroot);
...
doc.AppendChild(subroot2);
...
doc.AppendChild(subroot3);
which is not possible since an XML document can have only 1 root.
Add your root element like so:
XmlElement root = doc.CreateElement("LICENSE");
doc.AppendChild(root);
And change every doc.AppendChild() lateron into root.AppendChild()
And I have to agree with L.B.: LINQ to XML is much easier for stuff like this (but that wasn't the question ;) )

How to read in XML from web service and query rowset attributes in C#?

Here is the sample XML that I am receiving from the web service.
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly'>
<s:AttributeType name='NAME' rs:number='1'>
<s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='25'
rs:maybenull='false'/>
</s:AttributeType>
<s:AttributeType name='DESCRIPTION' rs:number='2' rs:nullable='true'
rs:writeunknown='true'>
<s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='80'/>
</s:AttributeType>
<s:extends type='rs:rowbase'/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row NAME='JOE BLOW' DESCRIPTION='PROGRAMMER'/>
<z:row NAME='ANN SMITH' DESCRIPTION='FRONT DESK'/>
<z:row NAME='STEVE JOHNSON' DESCRIPTION='TESTER'/>
</rs:data>
</xml>
I'm not sure how to go about getting to the <rs:data> node. I eventually want to enumerate through each <z:row> and get the attributes NAME and DESCRPTION. The webservice returns this to me as a string so I load the string into an XMLDocument like this:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xmlString);
Now, I'm not sure if I need to use a XmlNamespaceManager in order to tel my xDoc to import the <s:Schema>
I've tried to select a single node with an XPath with no luck because of the prefix rs.
XmlNode xNode = xDoc.SelectSingleNode("/rs:data");
If there is any further information needed, please ask.
If you are taking the xml as a string try parsing it like instead of using Load()
var xml = XDocument.Parse(xmlstring);
You have to use the namespace to get hold of the data properly.
XNamespace ns = "#RowsetSchema";
foreach (var element in xml.Descendants().Elements(ns + "row"))
{
Console.WriteLine ("Name = " + element.Attribute("NAME").Value + ", " + "Description = " + element.Attribute("DESCRIPTION").Value);
}
Using xml with linq it can be done like this:
XDocument xmlDoc = new XDocument();
xmlDoc = XDocument.Parse(xml);
XNamespace ns = "#RowsetSchema";
var namesDescs = from namesDesc in xmlDoc.Descendants().Elements(ns + "row")
select new
{
name = namesDesc.Attribute("NAME").Value,
description = namesDesc.Attribute("DESCRIPTION").Value,
};
Here's some classes for handling your xml, using these extensions: http://searisen.com/xmllib/extensions.wiki
public class Test
{
XElement self;
public Test(XElement self)
{
this.self = self;
}
public RSDataRow[] DataRows
{
get
{
if (null == _DataRows)
{
_DataRows = self.GetEnumerable("rs:data/z:row", xrow => new RSDataRow(xrow)).ToArray();
}
return _DataRows;
}
}
RSDataRow[] _DataRows;
}
[DebuggerDisplay("{Name}")]
public class RSDataRow
{
XElement self;
public RSDataRow(XElement self)
{
this.self = self;
}
public string Name
{
get { return self.Get("NAME", string.Empty); }
}
public string Description
{
get { return self.Get("DESCRIPTION", string.Empty); }
}
}
You use it like:
Test test = new Test(XElement.Parse(xmlstring));
foreach (RSDataRow row in test.DataRows)
{
string name = row.Name;
string desc = row.Description;
}

How to convert JSON to XML or XML to JSON?

I started to use Json.NET to convert a string in JSON format to object or viceversa. I am not sure in the Json.NET framework, is it possible to convert a string in JSON to XML format and viceversa?
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
Yes, you can do it (I do) but Be aware of some paradoxes when converting, and handle appropriately. You cannot automatically conform to all interface possibilities, and there is limited built-in support in controlling the conversion- many JSON structures and values cannot automatically be converted both ways. Keep in mind I am using the default settings with Newtonsoft JSON library and MS XML library, so your mileage may vary:
XML -> JSON
All data becomes string data (for example you will always get "false" not false or "0" not 0) Obviously JavaScript treats these differently in certain cases.
Children elements can become nested-object {} OR nested-array [ {} {} ...] depending if there is only one or more than one XML child-element. You would consume these two differently in JavaScript, etc. Different examples of XML conforming to the same schema can produce actually different JSON structures this way. You can add the attribute json:Array='true' to your element to workaround this in some (but not necessarily all) cases.
Your XML must be fairly well-formed, I have noticed it doesn't need to perfectly conform to W3C standard, but 1. you must have a root element and 2. you cannot start element names with numbers are two of the enforced XML standards I have found when using Newtonsoft and MS libraries.
In older versions, Blank elements do not convert to JSON. They are ignored. A blank element does not become "element":null
A new update changes how null can be handled (Thanks to Jon Story for pointing it out): https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_NullValueHandling.htm
JSON -> XML
You need a top level object that will convert to a root XML element or the parser will fail.
Your object names cannot start with a number, as they cannot be converted to elements (XML is technically even more strict than this) but I can 'get away' with breaking some of the other element naming rules.
Please feel free to mention any other issues you have noticed, I have developed my own custom routines for preparing and cleaning the strings as I convert back and forth. Your situation may or may not call for prep/cleanup. As StaxMan mentions, your situation may actually require that you convert between objects...this could entail appropriate interfaces and a bunch of case statements/etc to handle the caveats I mention above.
You can do these conversions also with the .NET Framework:
JSON to XML: by using System.Runtime.Serialization.Json
var xml = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(
Encoding.ASCII.GetBytes(jsonString), new XmlDictionaryReaderQuotas()));
XML to JSON: by using System.Web.Script.Serialization
var json = new JavaScriptSerializer().Serialize(GetXmlData(XElement.Parse(xmlString)));
private static Dictionary<string, object> GetXmlData(XElement xml)
{
var attr = xml.Attributes().ToDictionary(d => d.Name.LocalName, d => (object)d.Value);
if (xml.HasElements) attr.Add("_value", xml.Elements().Select(e => GetXmlData(e)));
else if (!xml.IsEmpty) attr.Add("_value", xml.Value);
return new Dictionary<string, object> { { xml.Name.LocalName, attr } };
}
I'm not sure there is point in such conversion (yes, many do it, but mostly to force a square peg through round hole) -- there is structural impedance mismatch, and conversion is lossy. So I would recommend against such format-to-format transformations.
But if you do it, first convert from json to object, then from object to xml (and vice versa for reverse direction). Doing direct transformation leads to ugly output, loss of information, or possibly both.
Thanks for David Brown's answer. In my case of JSON.Net 3.5, the convert methods are under the JsonConvert static class:
XmlNode myXmlNode = JsonConvert.DeserializeXmlNode(myJsonString); // is node not note
// or .DeserilizeXmlNode(myJsonString, "root"); // if myJsonString does not have a root
string jsonString = JsonConvert.SerializeXmlNode(myXmlNode);
I searched for a long time to find alternative code to the accepted solution in the hopes of not using an external assembly/project. I came up with the following thanks to the source code of the DynamicJson project:
public XmlDocument JsonToXML(string json)
{
XmlDocument doc = new XmlDocument();
using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(json), XmlDictionaryReaderQuotas.Max))
{
XElement xml = XElement.Load(reader);
doc.LoadXml(xml.ToString());
}
return doc;
}
Note: I wanted an XmlDocument rather than an XElement for xPath purposes.
Also, this code obviously only goes from JSON to XML, there are various ways to do the opposite.
Here is the full c# code to convert xml to json
public static class JSon
{
public static string XmlToJSON(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return XmlToJSON(doc);
}
public 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<string, object> childNodeNames = new SortedList<string, object>();
// 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)
{
List<object> alChild = (List<object>)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<string, object> 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
List<object> ValuesAL;
if (childNodeNames.ContainsKey(nodeName))
{
ValuesAL = (List<object>)childNodeNames[nodeName];
}
else
{
ValuesAL = new List<object>();
childNodeNames[nodeName] = ValuesAL;
}
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();
}
}
To convert a given XML string to JSON, simply call XmlToJSON() function as below.
string xml = "<menu id=\"file\" value=\"File\"> " +
"<popup>" +
"<menuitem value=\"New\" onclick=\"CreateNewDoc()\" />" +
"<menuitem value=\"Open\" onclick=\"OpenDoc()\" />" +
"<menuitem value=\"Close\" onclick=\"CloseDoc()\" />" +
"</popup>" +
"</menu>";
string json = JSON.XmlToJSON(xml);
// json = { "menu": {"id": "file", "popup": { "menuitem": [ {"onclick": "CreateNewDoc()", "value": "New" }, {"onclick": "OpenDoc()", "value": "Open" }, {"onclick": "CloseDoc()", "value": "Close" } ] }, "value": "File" }}
For convert JSON string to XML try this:
public string JsonToXML(string json)
{
XDocument xmlDoc = new XDocument(new XDeclaration("1.0", "utf-8", ""));
XElement root = new XElement("Root");
root.Name = "Result";
var dataTable = JsonConvert.DeserializeObject<DataTable>(json);
root.Add(
from row in dataTable.AsEnumerable()
select new XElement("Record",
from column in dataTable.Columns.Cast<DataColumn>()
select new XElement(column.ColumnName, row[column])
)
);
xmlDoc.Add(root);
return xmlDoc.ToString();
}
For convert XML to JSON try this:
public string XmlToJson(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc);
return jsonText;
}
Here is a simple snippet that converts a XmlNode (recursively) into a hashtable, and groups multiple instances of the same child into an array (as an ArrayList).
The Hashtable is usually accepted to convert into JSON by most of the JSON libraries.
protected object convert(XmlNode root){
Hashtable obj = new Hashtable();
for(int i=0,n=root.ChildNodes.Count;i<n;i++){
object result = null;
XmlNode current = root.ChildNodes.Item(i);
if(current.NodeType != XmlNodeType.Text)
result = convert(current);
else{
int resultInt;
double resultFloat;
bool resultBoolean;
if(Int32.TryParse(current.Value, out resultInt)) return resultInt;
if(Double.TryParse(current.Value, out resultFloat)) return resultFloat;
if(Boolean.TryParse(current.Value, out resultBoolean)) return resultBoolean;
return current.Value;
}
if(obj[current.Name] == null)
obj[current.Name] = result;
else if(obj[current.Name].GetType().Equals(typeof(ArrayList)))
((ArrayList)obj[current.Name]).Add(result);
else{
ArrayList collision = new ArrayList();
collision.Add(obj[current.Name]);
collision.Add(result);
obj[current.Name] = collision;
}
}
return obj;
}
Try this function. I just wrote it and haven't had much of a chance to test it, but my preliminary tests are promising.
public static XmlDocument JsonToXml(string json)
{
XmlNode newNode = null;
XmlNode appendToNode = null;
XmlDocument returnXmlDoc = new XmlDocument();
returnXmlDoc.LoadXml("<Document />");
XmlNode rootNode = returnXmlDoc.SelectSingleNode("Document");
appendToNode = rootNode;
string[] arrElementData;
string[] arrElements = json.Split('\r');
foreach (string element in arrElements)
{
string processElement = element.Replace("\r", "").Replace("\n", "").Replace("\t", "").Trim();
if ((processElement.IndexOf("}") > -1 || processElement.IndexOf("]") > -1) && appendToNode != rootNode)
{
appendToNode = appendToNode.ParentNode;
}
else if (processElement.IndexOf("[") > -1)
{
processElement = processElement.Replace(":", "").Replace("[", "").Replace("\"", "").Trim();
newNode = returnXmlDoc.CreateElement(processElement);
appendToNode.AppendChild(newNode);
appendToNode = newNode;
}
else if (processElement.IndexOf("{") > -1 && processElement.IndexOf(":") > -1)
{
processElement = processElement.Replace(":", "").Replace("{", "").Replace("\"", "").Trim();
newNode = returnXmlDoc.CreateElement(processElement);
appendToNode.AppendChild(newNode);
appendToNode = newNode;
}
else
{
if (processElement.IndexOf(":") > -1)
{
arrElementData = processElement.Replace(": \"", ":").Replace("\",", "").Replace("\"", "").Split(':');
newNode = returnXmlDoc.CreateElement(arrElementData[0]);
for (int i = 1; i < arrElementData.Length; i++)
{
newNode.InnerText += arrElementData[i];
}
appendToNode.AppendChild(newNode);
}
}
}
return returnXmlDoc;
}
I did like David Brown said but I got the following exception.
$exception {"There are multiple root elements. Line , position ."} System.Xml.XmlException
One solution would be to modify the XML file with a root element but that is not always necessary and for an XML stream it might not be possible either. My solution below:
var path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, #"..\..\App_Data"));
var directoryInfo = new DirectoryInfo(path);
var fileInfos = directoryInfo.GetFiles("*.xml");
foreach (var fileInfo in fileInfos)
{
XmlDocument doc = new XmlDocument();
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
using (XmlReader reader = XmlReader.Create(fileInfo.FullName, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
var node = doc.ReadNode(reader);
string json = JsonConvert.SerializeXmlNode(node);
}
}
}
}
Example XML that generates the error:
<parent>
<child>
Text
</child>
</parent>
<parent>
<child>
<grandchild>
Text
</grandchild>
<grandchild>
Text
</grandchild>
</child>
<child>
Text
</child>
</parent>
I have used the below methods to convert the JSON to XML
List <Item> items;
public void LoadJsonAndReadToXML() {
using(StreamReader r = new StreamReader(# "E:\Json\overiddenhotelranks.json")) {
string json = r.ReadToEnd();
items = JsonConvert.DeserializeObject <List<Item>> (json);
ReadToXML();
}
}
And
public void ReadToXML() {
try {
var xEle = new XElement("Items",
from item in items select new XElement("Item",
new XElement("mhid", item.mhid),
new XElement("hotelName", item.hotelName),
new XElement("destination", item.destination),
new XElement("destinationID", item.destinationID),
new XElement("rank", item.rank),
new XElement("toDisplayOnFod", item.toDisplayOnFod),
new XElement("comment", item.comment),
new XElement("Destinationcode", item.Destinationcode),
new XElement("LoadDate", item.LoadDate)
));
xEle.Save("E:\\employees.xml");
Console.WriteLine("Converted to XML");
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
I have used the class named Item to represent the elements
public class Item {
public int mhid { get; set; }
public string hotelName { get; set; }
public string destination { get; set; }
public int destinationID { get; set; }
public int rank { get; set; }
public int toDisplayOnFod { get; set; }
public string comment { get; set; }
public string Destinationcode { get; set; }
public string LoadDate { get; set; }
}
It works....
Cinchoo ETL - an open source library available to do the conversion of Xml to JSON easily with few lines of code
Xml -> JSON:
using (var p = new ChoXmlReader("sample.xml"))
{
using (var w = new ChoJSONWriter("sample.json"))
{
w.Write(p);
}
}
JSON -> Xml:
using (var p = new ChoJsonReader("sample.json"))
{
using (var w = new ChoXmlWriter("sample.xml"))
{
w.Write(p);
}
}
Sample fiddle: https://dotnetfiddle.net/enUJKu
Checkout CodeProject articles for some additional help.
Disclaimer: I'm the author of this library.
Here's an example of how to convert JSON to XML using .NET built-in libraries (instead of 3rd party libraries like Newtonsoft).
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Xml.Linq;
XDocument xmlDoc = jsonToXml(jsonObj);
private XDocument jsonToXml(JsonObject obj)
{
var xmlDoc = new XDocument();
var root = new XElement("Root");
xmlDoc.Add(root);
foreach (var prop in obj)
{
var xElement = new XElement(prop.Key);
xElement.Value = prop.Value.ToString();
root.Add(xElement);
}
return xmlDoc;
}

Categories