get xml node value from xml string - c#

I have xml which contain xml namespace. i need to get value from its xml node
<personxml:person xmlns:personxml="http://www.your.example.com/xml/person" xmlns:cityxml="http://www.my.example.com/xml/cities">
<personxml:name>Rob</personxml:name>
<personxml:age>37</personxml:age>
<cityxml:homecity>
<cityxml:name>London</cityxml:name>
<cityxml:lat>123.000</cityxml:lat>
<cityxml:long>0.00</cityxml:long>
</cityxml:homecity>
Now i want to get value of tag <cityxml:lat> as 123.00
Code :
string xml = "<personxml:person xmlns:personxml='http://www.your.example.com/xml/person' xmlns:cityxml='http://www.my.example.com/xml/cities'><personxml:name>Rob</personxml:name><personxml:age>37</personxml:age><cityxml:homecity><cityxml:name>London</cityxml:name><cityxml:lat>123.000</cityxml:lat><cityxml:long>0.00</cityxml:long></cityxml:homecity></personxml:person>";
var elem = XElement.Parse(xml);
var value = elem.Element("OTA_personxml/cityxml:homecity").Value;
Error i am getting
The '/' character, hexadecimal value 0x2F, cannot be included in a name.

You need to use XNamespace. For example:
XNamespace ns1 = "http://www.your.example.com/xml/person";
XNamespace ns2 = "http://www.my.example.com/xml/cities";
var elem = XElement.Parse(xml);
var value = elem.Element(ns2 + "homecity").Element(ns2 + "name").Value;
//value = "London"
Create XNamespace using a string that contains the URI, then combine the namespace with the local name.
For more information, see here.

You are better off using a XmlDocument to navigate your xml.
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNode node = doc.SelectSingleNode("//cityxml:homecity/cityxml:lat");
string latvalue = null;
if (node != null) latvalue = node.InnerText;

The error I got with your code was that there needs to be a namespace to parse the XML properly
Try :
XNamespace ns1 = "http://www.your.example.com/xml/cities";
string value = elem.Element(ns1 + "homecity").Element(ns1 + "name").Value;
I would still advice using XDocuments to parse if possible, but the above is fine if your way is a must.

Related

Pass XML data contained within CSV response

I'm downloading some CSV data as part of an API response and the last column of the CSV data contains an XML snippet as below:
<tns:Event xmlns:tns="http://someurl">
<tns:MaximumSeverity.Code>Error</tns:MaximumSeverity.Code>
<tns:EventItems>
<tns:EventItem>
<tns:Error.Code>ERRORCODE</tns:Error.Code>
<tns:Severity.Code>Error</tns:Severity.Code>
<tns:Short.Description>Short error</tns:Short.Description>
<tns:Detailed.Description>Longer error</tns:Detailed.Description>
<tns:Parameters></tns:Parameters>
</tns:EventItem>
</tns:EventItems>
</tns:Event>
I'd like to extract the text from either the short or detailed description so I'm wondering what the most efficient way is of doing this?
There could be multiple EventItem elements within the XML.
I tried the code below but got an XPathException:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlString);
XmlNodeList nodeList = doc.SelectNodes("/tns:Event");
foreach (XmlNode node in nodeList)
{
string errorTxt = node.SelectSingleNode("Short.Description").InnerText;
Console.WriteLine(errorTxt);
}
Console.ReadKey();
Use XDocument class which is much easier than XmlDocument, and you need to deal with xml namespace.
Something like this
using System.Xml.Linq;
XDocument xDoc = XDocument.Parse(xml); //xml is the string you pasted
XNamespace tns = "http://someurl";
var eventItems = xDoc.Element(tns + "Event").Element(tns + "EventItems").Elements(tns + "EventItem");
foreach (var eventItem in eventItems)
{
Console.WriteLine(eventItem.Element(tns + "Short.Description").Value);
Console.WriteLine(eventItem.Element(tns + "Detailed.Description").Value);
}
I tried the code by #kennyzx and its working
string xml = #"<tns:Event xmlns:tns=""http://someurl""><tns:MaximumSeverity.Code>Error</tns:MaximumSeverity.Code><tns:EventItems><tns:EventItem><tns:Error.Code>ERRORCODE</tns:Error.Code>
<tns:Severity.Code>Error</tns:Severity.Code>
<tns:Short.Description>Short error1</tns:Short.Description>
<tns:Detailed.Description>Longer error2</tns:Detailed.Description>
<tns:Parameters></tns:Parameters>
</tns:EventItem>
<tns:EventItem><tns:Error.Code>ERRORCODE</tns:Error.Code>
<tns:Severity.Code>Error</tns:Severity.Code>
<tns:Short.Description>Short error2</tns:Short.Description>
<tns:Detailed.Description>Longer error2</tns:Detailed.Description>
<tns:Parameters></tns:Parameters>
</tns:EventItem>
</tns:EventItems>
</tns:Event>";
XDocument xDoc = XDocument.Parse(xml);
XNamespace tns = "http://someurl";
var eventItems = xDoc.Element(tns + "Event").Element(tns + "EventItems").Elements(tns + "EventItem");
foreach (var eventItem in eventItems)
{
Console.WriteLine(eventItem.Element(tns + "Short.Description").Value);
Console.WriteLine(eventItem.Element(tns + "Detailed.Description").Value);
}

Get Atttribute value from XDocument

I am trying to get the ResponseCode attribute value out of this XML.
The XML is an XDocument
<IDMResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="1" xmlns="http://www.fake.org/namespace/">
<ARTSHeader>
<Response ResponseCode="Rejected">
<RequestID>1</RequestID>
<BusinessError Severity="Error">
<Code>IdmInvalidUserNamePasswordLoginProvided</Code>
<Description>Invalid username or password, if problem persists, please contact Administrator</Description>
</BusinessError>
</Response>
</ARTSHeader>
</IDMResponse>
With XPath: (No error checks done)
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("def", "http://www.fake.org/namespace/");
XDocument doc = XDocument.Parse(xml);
string code =
doc
.XPathSelectElement(#"/def:IDMResponse/def:ARTSHeader/def:Response", nsm)
.Attribute("ResponseCode")
.Value;
foreach (XElement el in doc.Root.Elements())
{
if(el.Name.ToString() == "ARTSHeader")
foreach(XElement ell in el.Elements())
{
if(ell.Name.ToString() == "Response")
string responseCode = ele.Attribute("ResponseCode").Value;
}
}
Does this work for you? I dont know the whole structure of your xml so you might need to go deeper into the nested xml to get to Response first
One possible way :
.....
XNamespace ns = "http://www.fake.org/namespace/";
string responseCode = (string)doc.Descendants(ns+"Response")
.First()
.Attribute("ResponseCode");
Console.WriteLine(responseCode);
You could try this one out, I haven`t tested so you might need to rearrange some structure
XDocument doc1 = XDocument.Parse(soapResult);
XNamespace ns1 = "http://www.fake.org/namespace/";
var items = doc1.Descendants(ns1 + "ARTSHeader").Descendants(ns1 + "Response").First().Attribute("ResponseCode").Descendants(ns1 + "BusinessError").First().Attribute("Severity")
.Select((x) => new
{
Code = x.Element(ns1 + "Code").Value,
Description = x.Element(ns1 + "Description").Value,
});

How to get value of element with XDocument and Linq to XML

I want to collect the RequestID element with the namespace, but I do not know how.
this.XmlString = "<?xml version=\"1.0\"
encoding=\"utf-8\"?><MethodNameRq xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><RequestID
xmlns=\"http://Mynamespace\">573-348976-428697-346</RequestID ></MethodNameRq>";
var doc = XDocument.Parse(this.XmlString);
this.RequestId = (string)doc.Descendants().Where(n => n.Name
== "RequestID ").FirstOrDefault();
This collects an empty string for RequestID. It does work if the string has no namespaces included. Does anyone know how I can collect the RequestID element?
you need to specify the namespace of your element
XNamespace ns = "http://Mynamespace";
this.RequestId = (string)doc.Descendants(ns + "RequestID").FirstOrDefault();

Reading attribute of an XML file

I have this XML file, I can read all the the nodes
<?xml version="1.0" encoding="UTF-8"?>
<cteProc xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
<CTe xmlns="http://www.portalfiscal.inf.br/cte">
<infCte versao="1.04" ID="CTe3512110414557000014604"></infCte>
</CTe>
</cteProc>
I have tried reading this using C#
string chavecte;
string CaminhoDoArquivo = #"C:\Separados\13512004-procCTe.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(CaminhoDoArquivo); //Carregando o arquivo
chavecte = xmlDoc.SelectSingleNode("infCTe")
.Attributes.GetNamedItem("Id").ToString();
but something is wrong with this code.
If you want to use Linq To Xml
var xDoc = XDocument.Load(CaminhoDoArquivo);
XNamespace ns = "http://www.portalfiscal.inf.br/cte";
var chavecte = xDoc.Descendants(ns+"infCte").First().Attribute("id").Value;
PS: I am assuming your xml's invalid line is as
<infCte versao="1.04" id="CTe3512110414557000014604"></infCte>
replace
chavecte = xmlDoc.SelectSingleNode("infCTe").Attributes.GetNamedItem("Id").Value;
with
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("ab", "http://www.portalfiscal.inf.br/cte");
chavecte = xmlDoc.SelectSingleNode("//ab:infCte", nsmgr)
.Attributes.GetNamedItem("Id").Value;
I've also noticed that infCte doesn't have the ID attribute properly defined in your xml
Another possible solution is:
string CaminhoDoArquivo = #"C:\Separados\13512004-procCTe.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(CaminhoDoArquivo); //Carregando o arquivo
// Select the node you're interested in
XmlNode node = xmlDoc.SelectSingleNode("/cteProc/CTe/infCte");
// Read the value of the attribute "ID" of that node
string chavecte = node.Attributes["ID"].Value;

Remove a portion of XML, edit it, then add back to xml at original position

So what I'm ultimately trying to do is parse XML and add element values to an element that is contained within a cdata section. I search for the cdata section within the xml pull it out and load it in another xdocument so as to keep the xml structure and then I add the element values but now I'm not sure how to add it back to the original xml at the original position.
Here is the original XMl:
<OUTPUT version="2.0"><RESPONSE><DATA state="FL" city="Sarasota">
<![CDATA[<LION xmlns="http://www.com" version="5.050">
<COMMENTS>
<PLACES>
Forest under a tree
</PLACES></COMMENTS></LION>]]>
</DATA></RESPONSE></OUTPUT>
I search for the cdata section and insert element values like this:
XDocument value = XDocument.Parse(returnValue);
RegexOptions options = RegexOptions.None;
Regex regex = new Regex(#"\<\!\[CDATA\[(?<text>[^\]]*)\]\]\>", options);
bool isMatch = regex.IsMatch(returnValue);
if(isMatch)
{
Match match = regex.Match(returnValue);
string HTMLtext = match.Groups["text"].Value;
XDocument cdata = XDocument.Parse(HTMLtext);
XNamespace ns = #"http://www";
var com = cdata.Descendants(ns + "COMMENTS").First();
var dcomm = com.Element(ns + "PLACES");
dcomm.Value = "test"+ dcomm.Value;
What I have left is to append back on the cdata text because that was removed converting regex to string and then place it back at the position of the cdata in the original xml.
You can test to see if the node is cdata without having to use regex using the NodeType property. In example we try to cast to XCData to test.
XElement root = XElement.Parse(input);
XElement dataElement = root.Descendants("DATA").FirstOrDefault();
XCData cdata = dataElement == null ? null : dataElement.FirstNode as XCData;
if (cdata == null)
{
return;
}
XElement nestedXml = XElement.Parse(cdata.Value);
XNamespace ns = #"http://www.com";
var com = nestedXml.Descendants(ns + "PLACES").First();
com.Value = "Incomplete App Email sent to member." + com.Value;
cdata.Value = nestedXml.ToString(SaveOptions.DisableFormatting);
string updatedOutput = cdata.ToString();

Categories