C# Trying to get XML inner text for nested node - c#

I can succesfully load the XMl document and traverse the nodes. Once I get the node that I want, I start setting values. How do I deal with nested nodes?
Here is the xml:
<incident>
<id>1234</id>
<number>5678</number>
<name>This is a name</name>
<state>Awaiting Input</state>
<priority>Medium</priority>
<category>
<id>99999</id>
<name>Applications</name>
<default_tags>applications</default_tags>
<parent_id nil="true" />
<default_assignee_id nil="true" />
</category>
Here is some C#:
id = node.SelectSingleNode("id").InnerText; //works fine
number = node.SelectSingleNode("number").InnerText; //works fine
name = node.SelectSingleNode("name").InnerText; //works fine
descHTML = node.SelectSingleNode("description").InnerText; //works fine
desc = node.SelectSingleNode("description_no_html").InnerText; //works fine
state = node.SelectSingleNode("state").InnerText; //works fine
priority = node.SelectSingleNode("priority").InnerText; //works fine
catagoryID = node.SelectSingleNode("category/id").InnerText; // null reference error
catagoryName = node.SelectSingleNode("category/name").InnerText; // null reference error
catagoryTags = node.SelectSingleNode("category/default_tags").InnerText; // null reference error

If you are reading different elements that may or may not exist, use ?. after the SelectSingleNode method. This will ensure you dont get the error Object Reference Not Set to an Instance of an object.
?. in essence checks if there is a value before proceeding to evaluate the next method or proprety.
string xml = #"<incident>
<id>1234</id>
<number>5678</number>
<name>This is a name</name>
<state>Awaiting Input</state>
<priority>Medium</priority>
<category>
<id>99999</id>
<name>Applications</name>
<default_tags>applications</default_tags>
<parent_id nil=""true"" />
<default_assignee_id nil=""true"" />
</category>
</incident>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var node = doc.DocumentElement;
var id = node.SelectSingleNode("id")?.InnerText; //works fine
var number = node.SelectSingleNode("number")?.InnerText; //works fine
var name = node.SelectSingleNode("name")?.InnerText; //works fine
var descHTML = node.SelectSingleNode("description")?.InnerText; //ERRORS because there is no description.
var desc = node.SelectSingleNode("description_no_html")?.InnerText; //works fine
var state = node.SelectSingleNode("state")?.InnerText; //works fine
var priority = node.SelectSingleNode("priority")?.InnerText; //works fine
var catagoryID = node.SelectSingleNode("//category/id")?.InnerText; // null reference error
var catagoryName = node.SelectSingleNode("//category/name")?.InnerText; // null reference error
var catagoryTags = node.SelectSingleNode("//category/default_tags")?.InnerText; // null reference error
Console.WriteLine($"name: {name}");
Console.WriteLine($"descHTML: {descHTML}");
Console.WriteLine($"desc: {desc}");
Console.WriteLine($"state: {state}");
Console.WriteLine($"priority: {priority}");
Console.WriteLine($"catagoryID: {catagoryID}");
Console.WriteLine($"catagoryName: {catagoryName}");
Console.WriteLine($"catagoryTags: {catagoryTags}");
Output it prints out
name: This is a name
descHTML:
desc:
state: Awaiting Input
priority: Medium
catagoryID: 99999
catagoryName: Applications
catagoryTags: applications
Code on #dotnetfiddle

I suppose you use the XmlDocument class and CATEGORY NODE is the only item that will have childnodes, so I have this method to go through each element, including subnodes (I hope this helps someone).
string xml =
#"<incident>
<id>1234</id>
<number>5678</number>
<name>This is a name</name>
<state>Awaiting Input</state>
<priority>Medium</priority>
<category>
<id>99999</id>
<name>Applications</name>
<default_tags>applications</default_tags>
<parent_id nil=""true"" />
<default_assignee_id nil=""true"" />
</category>
</incident>";
List<String> innerTextNode = new List<string>();
XmlDocument XmlDoc= new XmlDocument();
XmlDoc.LoadXml(xml);
XmlElement root = XmlDoc.DocumentElement;
XmlNodeList nodes = root.ChildNodes;
XmlNodeList childs;
foreach (XmlNode anode in nodes)
{
// The next is for any NODE that will have childnodes
// bool havechilds = anode.ChildNodes.OfType<XmlNode>().Any(x => x.NodeType != XmlNodeType.Text)
if (!anode.LocalName.Equals("category", StringComparison.CurrentCulture))
{
// The node is only text, no has childnodes
// So capturing InnerText
innerTextNode.Add(anode.InnerText);
}
else
{
childs = nodo.ChildNodes;
foreach (XmlNode childone in childs)
{
// So capturing InnerText
innerTextNode.Add(childone.InnerText);
}
}
}

Related

trying to read an XMLfile and only get the data out of one child node using c#

i am reading from an XML file and it pulls the correct data but pulls the data from 2 different nodes due to repeating of the attributes "server", "database" and "user"
When the program runs, it runs through both rather than just the one node i need it to run through. How can i make it just get the data from the node <MySql_Connection_string/>
The code is as follows:
XmlDocument doc = new XmlDocument();
doc.Load("xmlConnection.xml");
foreach (XmlNode node in doc.DocumentElement)
{
string databaseConnection = node.Attributes[0].InnerText;
if (databaseConnection == "MySQL")
{
foreach (XmlElement element in doc.DocumentElement)
{
foreach (XmlNode child in node.SelectNodes("//MySQL_Connection_string"))
{
MySQLServer = element.GetAttribute("Server");
MySQLDatabase = element.GetAttribute("Database");
MySQLUser = element.GetAttribute("user");
MySQLPassword = element.GetAttribute("password");
Console.WriteLine(MySQLServer);
Console.WriteLine(MySQLDatabase);
Console.WriteLine(MySQLUser);
Console.WriteLine(MySQLPassword);
}
}
}
}
The console.writeline is simply to see the outputs
the xml code is
<?xml version="1.0" encoding="utf-8"?>
<Database_Connections>
<MySQL_Connection_string Database_Connection="MySQL" Server="***" Database="littering_detection_jf" user="jordan" password="***" />
<PostgreSQL_Connection_string Database_Connection="postgreSQL" host="***" username="Jordan" password="***" database="intuvision" Port="***" />
<SQL_connection_string Database_Connection="SQL" Server="****" Database="anprdb" user="Jordan" password="***" />
</Database_Connections>
Get rid of all the foreach loops except the inner one:
foreach (XmlElement child in doc.SelectNodes("//MySQL_Connection_string"))
{
MySQLServer = child.GetAttribute("Server");
MySQLDatabase = child.GetAttribute("Database");
MySQLUser = child.GetAttribute("user");
MySQLPassword = child.GetAttribute("password");
Console.WriteLine(MySQLServer);
Console.WriteLine(MySQLDatabase);
Console.WriteLine(MySQLUser);
Console.WriteLine(MySQLPassword);
break; // not interested in any additional MySQL_Connection_string elements
}
This assumes that doc.SelectNodes("//MySQL_Connection_string") returns only XML elements, no other types of nodes which is the case here, so I replaced your XmlNode child by XmlElement child.

Read particular xml node using condition c#

I want to read particular node in XML like if any "Log"(root node) node contain "Message" node the it should read all the node under the "Log" node.
Note : Log node is root node and there are many node under "log" node.
for Example :
<TestLogDataSet>
<Log>
<Assembly>TestCase</Assembly>
<TestMethod>Application</TestMethod>
<Status>Passed</Status>
<Trace />
</Log>
<Log>
<Assembly>TestCase</Assembly>
<TestMethod>Application</TestMethod>
<Status>Failed</Status>
<Message>
<pre><![CDATA[ Error while deleting the Project]]>
</pre>
</Message>
<Trace />
</Log>
</TestLogDataSet>
Code :
string xmlFile = File.ReadAllText(#"D:\demo.xml");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlFile);
foreach (XmlNode lognode in xmlDoc.SelectNodes("/TestLogDataSet/Log[Message]"))
{
foreach (XmlNode node in lognode.ChildNodes)
{
string n1 = node.InnerText;
textBox1.Text = n1 + "\r\n";
}
}
You can use XPath for this.
StringBuilder nodeText = new StringBuilder();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(<your xml here>);
foreach (XmlNode lognode in xmlDoc.SelectNodes("/TestLogDataSet/Log[Message]")) //select all log nodes with Message as child tag
{
string status = lognode.SelectSingleNode("./Status").InnerText;
if (!string.Equals(status,"failed",StringComparison.OrdinalIgnoreCase))
{
continue;
}
foreach (XmlNode node in lognode.ChildNodes)
{
nodeText.Append(node.LocalName);
nodeText.Append(":");
nodeText.Append(node.InnerText);//read inner text of node here
nodeText.Append("\n");
}
}
Console.WriteLine(nodeText.ToString());
If you want the Log nodes then this should suffice:
var nodes =
xd
.Root
.Elements("Log")
.Where(x => x.Element("Message") != null);
That gives:
If you want a list of all of the child nodes (which is what I understand you want from your question, but it seems a bit odd) then this works:
var nodes =
xd
.Root
.Elements("Log")
.Where(x => x.Element("Message") != null)
.SelectMany(x => x.Elements());
This gives:

Why is this program not accessing child nodes?

Here it gets the XML document and individual nodes, and inserts the nodes into a dictionary.
//create the xml document obj
XmlDocument inputXMLDoc = new XmlDocument();
fileref.isValid = false;
//load the xml document
#region
try
{
inputXMLDoc.XmlResolver = null;
inputXMLDoc.Load( strfile );//load the xml file
string input = inputXMLDoc.OuterXml;//get the string
Console.WriteLine( "success,loaded XML" );
logger.Log( "loaded xml:" + strfile );
fileref.importList = new Dictionary<string, XmlNode>();
nodeNames = new List<string> { "OrderId", "CustomerId", "CustomerName", "Addresses", "OrderStatus", "DateOrdered", "PaymentTime", "IncludeVAT", "OrderTotalIncVat", "OrderTotalVat", "Currency", "TypeOfSaleId" };
try
{
int i = 0;
foreach( string name in nodeNames )
{
Console.WriteLine( "Adding xml node " + name );
if( inputXMLDoc.GetElementsByTagName( name ) != null )
{
XmlNodeList xlist = inputXMLDoc.GetElementsByTagName( name );
foreach( XmlNode node in xlist )
{
fileref.importList.Add( name, node );
//add individual node within nodelist
Console.WriteLine( name );
}
} //add specified node from XML doc
else
{
nodeNames.RemoveAt( i );
}
i++;
}
}
}
Later, the nodes are accessed to save the information to a web service. However, nodes with child nodes within are not showing up this way.
Invoices.Address address = new Invoices.Address();
XmlNodeList oNodeList = fileref.importList["Addresses"].SelectNodes("/Delivery/Street");
foreach (XmlNode xn in oNodeList)
{
address.Street = xn.InnerText;
}
Sample XML document
<?xml version="1.0" encoding="utf-8"?>
<InvoiceOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OrderId xmlns="http://24sevenOffice.com/webservices">35</OrderId>
<CustomerId xmlns="http://24sevenOffice.com/webservices">21</CustomerId>
<CustomerName xmlns="http://24sevenOffice.com/webservices">James Bond</CustomerName>
<Addresses xmlns="http://24sevenOffice.com/webservices">
<Delivery>
<Street>11 Shewell Walk</Street>
<State />
<PostalCode>CO1 1WG</PostalCode>
<PostalArea>Essex</PostalArea>
<Name />
<City>Colchester</City>
<Country>UK</Country>
</Delivery>
<Invoice>
<Street>10 Shewell Walk</Street>
<State />
<PostalCode>CO1 1WG</PostalCode>
<PostalArea>Essex</PostalArea>
<Name />
<City>Colchester</City>
<Country>UK</Country>
</Invoice>
</Addresses>
<OrderStatus xmlns="http://24sevenOffice.com/webservices">Offer</OrderStatus>
<DateOrdered xmlns="http://24sevenOffice.com/webservices">2015-06-15T14:00:00Z</DateOrdered>
<PaymentTime xmlns="http://24sevenOffice.com/webservices">14</PaymentTime>
<IncludeVAT xsi:nil="true" xmlns="http://24sevenOffice.com/webservices" />
<OrderTotalIncVat xmlns="http://24sevenOffice.com/webservices">480.0000</OrderTotalIncVat>
<OrderTotalVat xmlns="http://24sevenOffice.com/webservices">80.0000</OrderTotalVat>
<Currency xmlns="http://24sevenOffice.com/webservices">
<Symbol>LOCAL</Symbol>
</Currency>
<TypeOfSaleId xmlns="http://24sevenOffice.com/webservices">-100</TypeOfSaleId>
<InvoiceRows xmlns="http://24sevenOffice.com/webservices">
<InvoiceRow>
<ProductId>18</ProductId>
<RowId>4665754</RowId>
<Price>400.0000</Price>
<Name>17" Laptop Screen</Name>
<DiscountRate>0.0000</DiscountRate>
<Quantity>7.0000</Quantity>
<Cost>0.0000</Cost>
<InPrice>0.0000</InPrice>
</InvoiceRow>
</InvoiceRows>
</InvoiceOrder>
The reason your code doesn't work is likely that you're ignoring the namespace of the elements you're looking for. There are many questions covering how to do that, such as this one.
That said, XmlDocument is a creaky old API and the newer LINQ to XML is a huge improvement - I'd suggest you look into that.
I'm also not sure the dictionary is pulling its weight for such a small number of elements. You can simply query what you need straight from the XML. For example, to get all your fields as typed values:
var doc = XDocument.Parse(strfile);
var order = doc.Elements("InvoiceOrder").Single();
XNamespace ns = "http://24sevenOffice.com/webservices";
var orderId = (int)order.Element(ns + "OrderId");
var customerId = (int)order.Element(ns + "CustomerId");
var customerName = (string)order.Element(ns + "CustomerName");
var orderStatus = (string)order.Element(ns + "OrderStatus");
var dateOrdered = (DateTime)order.Element(ns + "DateOrdered");
var paymentTime = (int)order.Element(ns + "PaymentTime");
var totalIncVat = (decimal)order.Element(ns + "OrderTotalIncVat");
var totalVat = (decimal)order.Element(ns + "OrderTotalVat");
var currency = (string)order.Elements(ns + "Currency").Elements(ns + "Symbol").SingleOrDefault();
var typeOfSaleId = (int)order.Element(ns + "TypeOfSaleId");
You can use a similar technique to get map your addresses to your strongly typed Address class:
var deliveryAddress = order.Elements(ns + "Addresses")
.Elements(ns + "Delivery")
.Select(e => new Invoice.Address
{
Street = (string)e.Element(ns + "Street"),
// ....
})
.Single();
The problem you have is with namespaces. If you specify the namespace for each of those elements then it seems to work. I came to this conclusion with a bit of googling and some experimentation so my explanation might not be spot on so I advise researching the issue further yourself to understand it correctly.
This code will work:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(inputXMLDoc.NameTable);
nsmgr.AddNamespace("ns", "http://24sevenOffice.com/webservices");
var oNodeList = importList["Addresses"].SelectNodes("//ns:Delivery/ns:Street",nsmgr);
The reason is (I think) that in your XML document you are specifying a default namespace for your elements (xmlns="http://24sevenOffice.com/webservices") and in your xpath you are not specifying that same namespace. In my code I create a namespace manager with that namespace in and prefix it to the two elements which it now considers to match the ones in your document that have these namespaces.

Parse through XML

I've been reading through many tutorials and examples, but I'm lost. I have an XML file with this type of data:
<?xml version="1.0"?>
<properties>
<property>
<name>Client Property A</name>
<phone>Client Property A Phone Number</phone>
</property>
<property>
<name>Client Property B</name>
<phone>Client Property B Phone Number</phone>
</property>
<property>
<name>Client Property C</name>
<phone>Client Property C Phone Number</phone>
</property>
</properties>
I'm trying to parse through this data in C# but having no luck at all. I have this:
XmlTextReader xmldata = new XmlTextReader("http://url.to/xml");
XmlNodeList xmllist = doc.GetElementsByTagName("property");
processList( xmllist );
public void processList(XmlNodeList xmllist)
{
// Loop through each property node and list the information
foreach (XmlNode node in xmllist)
{
XmlElement nodeElement = (XmlElement)node;
txtBox.AppendText(nodeElement.GetElementsByTagName("name")[0].InnerText);
txtBox.AppendText(nodeElement.GetElementsByTagName("phone")[0].InnerText);
}
}
But nothing gets output in to my text box. :(
You can use Linq to Xml to get properties from your xml:
var xdoc = XDocument.Load("http://url.to/xml");
foreach(var p in xdoc.Root.Elements("property"))
{
txtBox.AppendText((string)p.Element("name"));
txtBox.AppendText((string)p.Element("phone"));
}
var m_strFilePath = "http://www.google.com/ig/api?weather=12414&hl=it";
string xmlStr;
using(var wc = new WebClient())
{
xmlStr = wc.DownloadString(m_strFilePath);
}
var doc= new XmlDocument();
doc.LoadXml(xmlStr);
XmlNodeList xmllist = doc.SelectNodes("//property");
processList( xmllist );
public void processList(XmlNodeList xmllist)
{
// Loop through each property node and list the information
foreach (XmlNode node in xmllist)
{
XmlElement nodeElement = (XmlElement)node;
txtBox.AppendText(nodeElement.SelectSingleNode("name").InnerText);
txtBox.AppendText(nodeElement.SelectSingleNode("phone").InnerText);
}
}

Get XmlNodeList if a particular element value or its attribute value is present in a given list of strings

I would like to get XmlNodeList from a huge XML file.
Conditions:
I have a List of unique ID values, say IDList
Case I: Collect all the nodes where element called ID has value from IDList.
Case II: Collect all nodes where one of the attribute called idName of element ID has value from IDList.
In short, extract only the nodes which match with the values given in the IDList.
I did this using some loops like load this XML to XmlDocument to iterate over all nodes and ID value but what I am looking for is some sophisticated method to do it faster and in quick way.
Because looping isn't a solution for a large XML file.
My try:
try
{
using (XmlReader reader = XmlReader.Create(URL))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList nodeList = doc.GetElementsByTagName("idgroup");
foreach (XmlNode xn in nodeList)
{
string id = xn.Attributes["id"].Value;
string value = string.Empty;
if (IDList.Contains(id))
{
value = xn.ChildNodes[1].ChildNodes[1].InnerText; // <value>
if (!string.IsNullOrEmpty(value))
{
listValueCollection.Add(value);
}
}
}
}
}
catch
{}
XML (XLIFF) structure:
<XLIFF>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">
<file date="2013-07-17">
<body>
<id idName="test_001" >
<desc-group name="test_001">
<desc type="text"/>
</desc-group>
<result-unit idName="test_001_text">
<source>abcd</source>
<result>xyz</result>
</result-unit>
</id>
</body>
</file>
</xliff>
Collect all the nodes like above where idName matches.
EDIT
This is a test that can parse the example you are giving. It attempts to reach the result node directly, so that it stays as efficient as possible.
[Test]
public void TestXPathExpression()
{
var idList = new List<string> { "test_001" };
var resultsList = new List<string>();
// Replace with appropriate method to open your URL.
using (var reader = new XmlTextReader(File.OpenRead("fixtures\\XLIFF_sample_01.xlf")))
{
var doc = new XmlDocument();
doc.Load(reader);
var root = doc.DocumentElement;
// This is necessary, since your example is namespaced.
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("x", "urn:oasis:names:tc:xliff:document:1.2");
// Go directly to the node from which you want the result to come from.
foreach (var nodes in idList
.Select(id => root.SelectNodes("//x:file/x:body/x:id[#idName='" + id + "']/x:result-unit/x:result", nsmgr))
.Where(nodes => nodes != null && nodes.Count > 0))
resultsList.AddRange(nodes.Cast<XmlNode>().Select(node => node.InnerText));
}
// Print the resulting list.
resultsList.ForEach(Console.WriteLine);
}
You can extract only those nodes you need by using an XPath query. A brief example on how you 'd go about it:
using (XmlReader reader = XmlReader.Create(URL))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
foreach(var id in IDList) {
var nodes = doc.SelectNodes("//xliff/file/body/id[#idName='" + id + "']");
foreach(var node in nodes.Where(x => !string.IsNullOrEmpty(x.ChildNodes[1].ChildNodes[1].InnerText)))
listValueCollection.Add(node.ChildNodes[1].ChildNodes[1].InnerText);
}
}
The xpath expression is of course an example. If you want, you can post an example of your XML so I can give you something more accurate.

Categories