Convert Soap XML to a Json Object in C# - c#

Background Information
I have two .net services (say A and B). Service B uses a service reference of Service A. Here, 'basicHttpBinding' is being used.
There is a global.asax.cs present in Service A where I plan to perform some operations before the call is sent to Service A.svc.cs
I'm able to read request body in global.asax.cs using the following code.
StreamReader streamReader = new StreamReader(HttpContext.Current.Request.InputStream);
streamReader.BaseStream.Position = 0;
string message = streamReader.ReadToEnd();
The 'message' string variable holds the request body i.e. payload in xml format. I'm able to read the xml using the following code.
XmlDocument doc = new XmlDocument();
doc.LoadXml(message);
The xml looks like this
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<FunctionName xmlns="http://tempuri.org/">
<sampleString>value</sampleString>
<sampleObject xmlns:a="http://schemas.datacontract.org/2004/07/contract" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:sampleProperty1>value1</a:sampleProperty1>
<a:sampleProperty2>value2</a:sampleProperty2>
</sampleObject>
</FunctionName>
</s:Body>
</s:Envelope>
Question
Is there any way to convert this xml to json? I'm only interested in the data inside in the xml.
Bonus Question
What does 'a:' in 'a:sampleProperty' mean / stand for?
Desired Output
The final json should like this
{
"sampleString": "value",
"sampleObject": {
"sampleProperty1": "value1",
"sampleProperty2": "value2"
}
}
Things that I have tried
I have tried removing top parent nodes and their attributes using code. Then, I used to JsonConvert to convert xml to json
JsonConvert.SerializeXmlNode(doc.ChildNodes[0].ChildNodes[0].ChildNodes[0], Newtonsoft.Json.Formatting.None, true);
Doing this only helped me partially and I ended with the following json output
{
"sampleString": "value",
"sampleObject": {
"#xmlns:a":"http://schemas.datacontract.org/2004/07/contract",
"#xmlns:i":"http://www.w3.org/2001/XMLSchema-instance",
"a:sampleProperty1": "value1",
"a:sampleProperty2": "value2"
}
}

See the accepted answer here to remove the namespaces from the XML, Define a method RemoveAllNamespaces and change the code like below -
XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(message));
var xmlWithoutNs = xmlDocumentWithoutNs.ToString();
/* OUTPUT
<Envelope>
<Body>
<FunctionName>
<sampleString> value </sampleString>
<sampleObject>
<sampleProperty1> value1 </sampleProperty1>
<sampleProperty2> value2 </sampleProperty2>
</sampleObject>
</FunctionName>
</Body>
</Envelope>
*/
var json =JsonConvert.SerializeXmlNode(doc.ChildNodes[0].ChildNodes[0].ChildNodes[0], Newtonsoft.Json.Formatting.None, true);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlWithoutNs);
/* OUTPUT
{
"sampleString":" value ",
"sampleObject":{
"sampleProperty1":" value1 ",
"sampleProperty2":" value2 "
}
}
*/
To answer your question -
What does 'a:' in 'a:sampleProperty' mean / stand for?
A colon (:) in a tag or attribute name means that the element or attribute is in an XML namespace.The colon, and the part before it, aren't really part of the tag / attribute name, they just indicate which namespace it's in.
https://en.wikipedia.org/wiki/XML_namespace
Discussions here

Related

Check whether JSON property contains value or array

I have a random input XML file which I am trying to convert into JSON and access values. Now JSON might have a simple JSON object or it might have array within it. When I am working with simple JSON object I am able to get values properly but when any JSON property contains an array I am getting an error that "value cannot be null". I am using newtonsoft library for this. My question is how to know whether a property is having a value or an array in JSON when every time the input file might have different structure. I hope I am able to explain my problem and the code I am using is given below:
XmlDocument Xmldoc = new XmlDocument();
Xmldoc.LoadXml(fileContents);
string root = Xmldoc.DocumentElement.Name;
var json = JsonConvert.SerializeXmlNode(Xmldoc);
var JsonStringObject = (JObject)JsonConvert.DeserializeObject(json);
var XMLNodesData = JsonStringObject[root.ToString()].Children();
List<JToken> tokens = XMLNodesData.Children().Children().ToList();
foreach (var node in tokens)
{
foreach (Field field in fields)
{
var inputString = node[field.FieldName].Value<string>();
}
}
With above code I am able to get value properly on inputString if I am having a simple XML such as :
<PatientRecords>
<Patient>
<firstname>John</firstname>
<lastname>Smith</lastname>
<patientid>111</patientid>
<dob>2022-05-13</dob>
</Patient>
<Patient>
<firstname>Martha</firstname>
<lastname>Stewart</lastname>
<patientid>112</patientid>
<dob>2022-04-14</dob>
</Patient>
</PatientRecords>
but if I Change XML to below given format then I am getting an error "Value cannot be null" incase of age property:
<PatientRecords>
<Patient>
<firstname>John</firstname>
<lastname>Smith</lastname>
<patientid>111</patientid>
<age>
<dob>2022-05-13</dob>
</age>
</Patient>
<Patient>
<firstname>Martha</firstname>
<lastname>Stewart</lastname>
<patientid>112</patientid>
<age>
<dob>2022-04-14</dob>
</age>
</Patient>
</PatientRecords>
Note: field.Fieldname is used to get tag name ("firstname", "lastname" etc) from a different XML which is required in my project. Please help regarding this and thank you in advance.
You can try below
var jsonResult = JsonConvert.DeserializeObject<dynamic>(jsonStr);
if(jsonResult.Type==JTokenType.Object)
{
//JSON contains Object
}
else if(jsonResult.Type==JTokenType.Array)
{
//JSON contains Array
}

Include XML CDATA in an element

UPDATE: Added more detail per request
I am trying to create an xml configuration file for my application. The file contains a list of criteria to search and replace in an html document. The problem is, I need to search for character strings like &nbsp. I do not want my code to read the decoded item, but the text itself.
Admitting to being very new to XML, I did make some attempts at meeting the requirements. I read a load of links here on Stackoverflow regarding CDATA and ATTRIBUTES and so on, but the examples here (and elsewhere) seem to focus on creating one single line in an xml file, not multiple.
Here is one of many attempts I have made to no avail:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE item [
<!ELEMENT item (id, replacewith)>
<!ELEMENT id (#CDATA)>
<!ELEMENT replacewith (#CDATA)>
]>
]>
<item id=" " replacewith=" ">Non breaking space</item>
<item id="‑" replacewith="-">Non breaking hyphen</item>
This document gives me a number of errors, including:
In the DOCTYPE, I get errors like <!ELEMENT id (#CDATA)>. In the CDATA area, Visual Studio informs me it is expecting a ',' or '|'.
]> gives me an error of invalid token at the root of the document.
And of course, after the second <item entry, I get an error stating XML document cannot contain multiple root level elements.
How can I write an xml file that includes multiple items and allows me to store and retrieve the text within the element, rather than the interpreted characters?
If it helps any, I am using .Net, C#, and Visual Studio.
EDIT:
The purpose of this xml file is to provide my code with a list of things to search and replace in an html file. The xml file simply contains a list of what to search for and what to replace with.
Here is the file I have in place right now:
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<item id="‑" replacewith="-">Non breaking hyphen</item>
<item id=" " replacewith=" ">Non breaking hyphen</item>
</Items>
Using the first as an example, I want to read the text &#8209 but instead when I read this, I get - because that is what the code represents.
Any help or pointers you can give would be helpful.
To elaborate on my comment: XML acts like HTML due to the reserved characters. An ampersand prefixes keywords or character codes to translate into a literal string when read in with any type of parser (browser, XML reader, etc).
The easiest way to escape the values to make sure they are read back in as the literal that you want is to put them in as if you were encoding it for web. For example, to create your XML document, I did this:
XmlDocument xmlDoc = new XmlDocument();
XmlElement xmlItem;
XmlAttribute xmlAttr;
XmlText xmlText;
// Declaration
XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
XmlElement xmlRoot = xmlDoc.DocumentElement;
xmlDoc.InsertBefore(xmlDec, xmlRoot);
// Items
XmlElement xmlItems = xmlDoc.CreateElement(string.Empty, "Items", string.Empty);
xmlDoc.AppendChild(xmlItems);
// Item #1
xmlItem = xmlDoc.CreateElement(string.Empty, "item", string.Empty);
xmlAttr = xmlDoc.CreateAttribute(string.Empty, "id", string.Empty);
xmlAttr.Value = "‑";
xmlItem.Attributes.Append(xmlAttr);
xmlAttr = xmlDoc.CreateAttribute(string.Empty, "replacewith", string.Empty);
xmlAttr.Value = "-";
xmlItem.Attributes.Append(xmlAttr);
xmlText = xmlDoc.CreateTextNode("Non breaking hyphen");
xmlItem.AppendChild(xmlText);
xmlItems.AppendChild(xmlItem);
// Item #2
xmlItem = xmlDoc.CreateElement(string.Empty, "item", string.Empty);
xmlAttr = xmlDoc.CreateAttribute(string.Empty, "id", string.Empty);
xmlAttr.Value = " ";
xmlItem.Attributes.Append(xmlAttr);
xmlAttr = xmlDoc.CreateAttribute(string.Empty, "replacewith", string.Empty);
xmlAttr.Value = " ";
xmlItem.Attributes.Append(xmlAttr);
xmlText = xmlDoc.CreateTextNode("Non breaking hyphen");
xmlItem.AppendChild(xmlText);
xmlItems.AppendChild(xmlItem);
// For formatting
StringBuilder xmlBuilder = new StringBuilder();
XmlWriterSettings xmlSettings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace
};
using (XmlWriter writer = XmlWriter.Create(xmlBuilder, xmlSettings))
{
xmlDoc.Save(writer);
}
xmlOutput.Text = xmlBuilder.ToString();
Notice that I put in your id values with what you are expecting. Now, look at how it gets encoded:
<?xml version="1.0" encoding="utf-16"?>
<Items>
<item id="&#8209;" replacewith="-">Non breaking hyphen</item>
<item id=" " replacewith="&nbsp;">Non breaking hyphen</item>
</Items>
The only difference between yours and this one is that the ampersand was encoded as & and the rest remained as a string literal. This is normal behavior for XML. When you read it back in, it will come back as the literal ‑ and .

Navigating using XPath stored as a string

I am trying to store XPath of an XML attribute as a string in a separate file so that if XPath changes, I can easily modify the navigation to the attribute without changing code.
For example, in following XML:
<Result>
<Server = "main">
<Client id="1"></Client>
</Server>
</Result>
if I want to navigate to id attribute of Client element, I can have following string:
Result->Server->Client->id
I am not sure how in C# I can navigate using this string form of XPath and then, read the attribute value from the target XML.
Please help.
Harit
Well, firstly, your XML is a bit strange, with
<Server = "main">
Do you mean
<Server id="main">
But, regardless of that, you could just store the XPath directly instead of your string version. Like:
/Result/Server/Client[0]/#id
then you read the string from the file and pass it into something like:
public string GetClientIdString(string xPathString)
{
var doc = new XmlDocument();
doc.Load("SomeXml.xml");
return doc.DocumentElement.SelectSingleNode(xPathString).Value;
}
The issue becomes that you can't really store the XPath exactly how you would like if you plan on having more than one Client under Server. If you need that functionality, though, you could parse out your version of the XPath and do something like:
public IEnumerable<string> GetClientIdStrings(string elementXPath, string attribute)
{
var doc = new XmlDocument();
doc.Load(SomeXml.xml);
var clientIdStrings = new List<string>();
foreach(var node in doc.DocumentElement.SelectNodes(elementXPath))
{
clientIdStrings.Add(node.Attributes[attribute].Value);
}
return clientIdStrings;
}

Unable to XML serialization when using .NET to consume REST Axis2 WS

I'm trying to consume an Axis2 REST XML response using C#.NET with RestSharp and Linq. However I can't seem to serialize the XML response either using RestSharp or manually.
This is an example of the XML response from Axis2:
<ns:response xmlns:ns="http://com.some.where" xmlns:ax2488="http://com.some.where/xsd">
<ns:return xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax2488:Book">
<ax2488:field1>Orson Scott Card</ax2488:field1>
<ax2488:field1>Some One Else</ax2488:field1>
<ax2488:field2>1</ax2488:field2>
<ax2488:isbn10>142996393X</ax2488:isbn10>
<ax2488:isbn13>9781429963930</ax2488:isbn13>
<ax2488:date>2010</ax2488:date>
<ax2488:blah>Tom Doherty Associates</ax2488:blah>
<ax2488:ssss>on loan</ax2488:ssss>
<ax2488:name>Ender's Game Volume 1 of The Ender Quintet</ax2488:name>
</ns:return>
When I try to get the elements out there using XElements (Linq), I always get null?
var elements = root.Elements("Book");
I've tried a few different element names with no luck.
Also, using RestSharp, it successfully gets the first and only response, however if I have multiple responses, it returns null.
var response1 = _client.Execute<Book>(request);
var response2 = _client.Execute<List<Book>>(request);
Any help appreciated.
One possible way, using XDocument with XPath query :
using System.Xml.XPath;
var nsmgr = new XmlNamespaceManager(new NameTable());
//register prefixes for use in XPath
nsmgr.AddNamespace("ns", "http://com.some.where");
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
var xpath = "//ns:response/ns:return[#xsi:type='ax2488:Book']";
//above xpath consists of path from root to <ns:return> : //ns:response/ns:return
//and criteria for <ns:return> : [#xsi:type='ax2488:Book']
var doc = XDocument.Load("path_to_xml_file.xml");
var returnElements = doc.XPathSelectElements(xpath, nsmgr);
I assumed that your XML structure looks about like this :
<root>
<ns:response xmlns:ns="http://com.some.where" xmlns:ax2488="http://com.some.where/xsd">
<ns:return xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax2488:Book">
<ax2488:field1>Orson Scott Card</ax2488:field1>
<ax2488:field1>Some One Else</ax2488:field1>
<ax2488:field2>1</ax2488:field2>
<ax2488:isbn10>142996393X</ax2488:isbn10>
<ax2488:isbn13>9781429963930</ax2488:isbn13>
<ax2488:date>2010</ax2488:date>
<ax2488:blah>Tom Doherty Associates</ax2488:blah>
<ax2488:ssss>on loan</ax2488:ssss>
<ax2488:name>Ender's Game Volume 1 of The Ender Quintet</ax2488:name>
</ns:return>
</ns:response>
<ns:response xmlns:ns="http://com.some.where" xmlns:ax2488="http://com.some.where/xsd">
........
</ns:response>
........
........
</root>

JsonConvert.DeserializeXmlNode - must begin with an object

I'm getting a JsonSerializationException calling DeserializeXmlNode() on JSON data that starts with [[ (i.e. it's an array of arrays).
What is the best
way to turn this into XML?
Are there any other JSON schemas that can't be turned into XML?
Update: How the XML should appear is an interesting question. Having an array of arrays means there is no root node (that's an easy one - insert ) but also the set of children nodes have no name. I'm not sure what makes sense here. And this may be a deal killer for using XPath on JSON. So on this part too, any suggestions?
Update 2 - the JSON data:
[["P0010001","NAME","state"],
["4779736","Alabama","01"],
["710231","Alaska","02"],
["6392017","Arizona","04"],
["2915918","Arkansas","05"],
["37253956","California","06"],
["5029196","Colorado","08"],
["3574097","Connecticut","09"],
["897934","Delaware","10"],
["601723","District of Columbia","11"],
["18801310","Florida","12"],
["9687653","Georgia","13"],
["1360301","Hawaii","15"],
["1567582","Idaho","16"],
["12830632","Illinois","17"],
["6483802","Indiana","18"],
["3046355","Iowa","19"],
["2853118","Kansas","20"],
["4339367","Kentucky","21"],
["4533372","Louisiana","22"],
["1328361","Maine","23"],
["5773552","Maryland","24"],
["6547629","Massachusetts","25"],
["9883640","Michigan","26"],
["5303925","Minnesota","27"],
["2967297","Mississippi","28"],
["5988927","Missouri","29"],
["989415","Montana","30"],
["1826341","Nebraska","31"],
["2700551","Nevada","32"],
["1316470","New Hampshire","33"],
["8791894","New Jersey","34"],
["2059179","New Mexico","35"],
["19378102","New York","36"],
["9535483","North Carolina","37"],
["672591","North Dakota","38"],
["11536504","Ohio","39"],
["3751351","Oklahoma","40"],
["3831074","Oregon","41"],
["12702379","Pennsylvania","42"],
["1052567","Rhode Island","44"],
["4625364","South Carolina","45"],
["814180","South Dakota","46"],
["6346105","Tennessee","47"],
["25145561","Texas","48"],
["2763885","Utah","49"],
["625741","Vermont","50"],
["8001024","Virginia","51"],
["6724540","Washington","53"],
["1852994","West Virginia","54"],
["5686986","Wisconsin","55"],
["563626","Wyoming","56"],
["3725789","Puerto Rico","72"]]
I had an array of objects, shaped like:
[{foo:bar}, {foo:bar2}]
...What I did to work around this problem is to wrap the text first like so:
public XmlDocument JsonArrayToXml(string json)
{
var wrappedDocument = string.Format("{{ item: {0} }}", json);
var xDocument = JsonConvert.DeserializeXmlNode(wrappedDocument, "collection");
return xDocument;
}
This does not throw an error. The shape of the XML resembles:
<?xml version="1.0" encoding="UTF-8"?>
<collection>
<item>
<foo>bar</foo>
</item>
<item>
<foo>bar2</foo>
</item>
</collection>

Categories

Resources