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);
}
Related
I am parsing below XML. I need value 'x2#email.com'.I am able to get the list of nodes successfully, but the problem is that with each iteration I am still getting 'x1#email.com' from the first group of 'Info' element.
XML:
<ClaimAdminContact xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Services.Models">
<claimAdminID>T1</claimAdminID>
<contactInfo>
<Info>
<desc>Level 1 Notifications</desc>
<emailAddress>x1#email.com</emailAddress>
<orgNum>1234</orgNum>
<type>T2</type>
</Info>
<Info>
<desc>Level 2 Notifications</desc>
<emailAddress>x2#email.com</emailAddress>
<orgNum i:nil="true"/>
<type>T2</type>
</Info>
<Info>
<desc>Level 3 Notifications</desc>
<emailAddress>x3#email.com</emailAddress>
<orgNum i:nil="true"/>
<type>T2</type>
</Info>
</contactInfo>
</ClaimAdminContact>
I have tried full xpath but still not able to get the next set of values. Below is the code that I am using to parse xml.
Code:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlEmail.NameTable);
nsmgr.AddNamespace("MsBuild", xmlns);
var contactInfo = xmlEmail.SelectNodes("/MsBuild:ClaimAdminContact/MsBuild:contactInfo/*", nsmgr);
foreach (XmlNode item in contactInfo)
{
_notificationDesc = item.SelectSingleNode("//MsBuild:desc", nsmgr).InnerText;
_reviewEmail = item.SelectSingleNode("//MsBuild:emailAddress", nsmgr).InnerText;
_orgNum = item.SelectSingleNode("//MsBuild:orgNum", nsmgr).InnerText;
}
Please
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns = doc.Root.GetDefaultNamespace();
var results = doc.Descendants(ns + "Info").Select(x => new
{
desc = (string)x.Element(ns + "desc"),
email = (string)x.Element(ns + "emailAddress"),
orgNum = (string)x.Element(ns + "orgNum"),
type = (string)x.Element(ns + "type")
}).ToList();
}
}
}
Using System.Xml.Linq:
var xmlFile = #"myxml.xml";
var xDoc = XDocument.Load(xmlFile);
var infos = xDoc.Descendants("Info");
foreach (var info in infos)
{
var email = info.Element("emailAddress").Value;
}
EDIT: How to work with namespaces
If you have multiple namespaces and want to work with that, then you must specify the namespaces like below. Removing namespaces from the file is hardly a good idea.
var xmlFile = #"C:\Users\gurudeniyas\Desktop\myxml.xml";
XNamespace ns = "http://schemas.datacontract.org/2004/07/Services.Models";
XNamespace nsi = "http://www.w3.org/2001/XMLSchema-instance";
var xDoc = XDocument.Load(xmlFile);
var infos = xDoc.Descendants(ns + "Info");
foreach (var info in infos)
{
var email = info.Descendants(ns + "emailAddress").FirstOrDefault().Value;
Console.WriteLine(email);
}
The main challenge I was facing in parsing xml was the namespace attribute in the root element. It was preventing my code to parse the usual way and that was the reason why I tried using 'XmlNamespaceManager'. I decided to remove the namespace from the xml.
I used below recursive method to remove namespace from xml and everything worked!! I am not sure if this is the optimal way, but I was able to achieve what I wanted.
public XElement RemoveAllNamespaces(XElement root)
{
return new XElement(
root.Name.LocalName,
root.HasElements ?
root.Elements().Select(x => RemoveAllNamespaces(x)) :
(object)root.Value
);
}
Calling Code:
XElement noNsDoc = RemoveAllNamespaces(XElement.Parse(xmlString));
var xDoc = XDocument.Parse(noNsDoc.ToString());
I am working on this since yesterday. I have an XML file which looks something like this
<catalog>
<captureInfo>
<row>5</row>
<col>5</col>
</captureInfo>
<patientInfo>
<name>XYZ</name>
<detail>details here</detail>
</patientInfo>
<imageData>
<r0c0>
<contrastFlag>true</contrastFlag>
</r0c0>
<r0c1>
<contrastFlag>true</contrastFlag>
</r0c1>
</imageData>
</catalog>
I need to update the value of contrastFlag in the XML file. This is the code I have written:
XmlDocument doc = new XmlDocument();
XmlNodeList imageData = doc.GetElementsByTagName("imageData");
foreach(XmlNode node in imageData)
{
foreach (XmlNode innernode in node)
{
if (innernode.Name == "r0c0")
{
innernode.InnerText = "false";
}
}
}
doc.Save("XMLFile1.xml");
Can anyone tell me where am I going wrong and also is there any better/faster approach for this?
Well first off, your XML is malformed, the closing should match "catalog". Why not just do this:
string xml = #"<catalog>
<captureInfo>
<row>5</row>
<col>5</col>
</captureInfo>
<patientInfo>
<name>XYZ</name>
<detail>details here</detail>
</patientInfo>
<imageData>
<r0c0>
<contrastFlag>true</contrastFlag>
</r0c0>
<r0c1>
<contrastFlag>true</contrastFlag>
</r0c1>
</imageData>
</catalog>";
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
xdoc.SelectSingleNode("//catalog/imageData/r0c0/contrastFlag").InnerText = "false";
Here is a way to replace all of the instances using LINQ. I just wrote out to a new file to preserve the source.
StreamReader stream = new StreamReader(#"c:\test.xml");
XDocument doc = XDocument.Load(stream);
IEnumerable<XElement> flags = doc.Descendants("contrastFlag");
foreach (XElement e in flags)
{
e.Value = "false";
}
doc.Save(#"c:\test2.xml");
I am writing a program that reads a XML file with Visual C#. I have a problem reading the Xml file, because it contains invalid XML symbols, for example '&'.
I have to read the XML but I can not modify the document. How can I modify the Xml file using C#? My code so far:
private void button1_Click(object sender, EventArgs e)
{
XmlDocument doc;
doc = new XmlDocument();
doc.Load("nuevo.xml");
XmlNodeList Xpersonas = doc.GetElementsByTagName("personas");
XmlNodeList Xlista = ((XmlElement)Xpersonas[0]).GetElementsByTagName("edad");
foreach (XmlElement nodo in Xlista)
{
string edad = nodo.GetAttribute("edad");
string nombre = nodo.InnerText;
textBox1.Text = nodo.InnerXml;
}
As #EBrown suggested, one possibility would be read the file content in a string variable and replace the & symbol with the correct representation for propert XML & and then parse the XML structure. A possible solution could look like this:
var xmlContent = File.ReadAllText(#"nuevo.xml");
XmlDocument doc;
doc = new XmlDocument();
doc.LoadXml(xmlContent.Replace("&", "&"));
XmlNodeList Xpersonas = doc.GetElementsByTagName("personas");
XmlNodeList Xlista = ((XmlElement)Xpersonas[0]).GetElementsByTagName("edad");
foreach (XmlElement nodo in Xlista)
{
string edad = nodo.GetAttribute("edad");
string nombre = nodo.InnerText;
Console.WriteLine(nodo.InnerXml.Replace("&", "&"));
}
The output is:
34 & 34
If it is ok to use LINQ2XML, then the solution is even shorter, and there is no need to write the reverse(second) replace, because LINQ2XML make this for you automatically:
var xmlContent = File.ReadAllText(#"nuevo.xml");
var xmlDocument = XDocument.Parse(xmlContent.Replace("&", "&"));
var edad = xmlDocument.Root.Element("edad").Value;
Console.WriteLine(edad);
The output is the same as above.
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,
});
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.