Iterate through XmlNodeList, value is always the same - c#

I have the following XML:
<xmlRequest>
<stats>
<match mid='40704828'>
<match_stats>
<ms aid='254664' cli_name='Hero_Engineer'>
<stat name='nickname'>lethallynx</stat>
<stat name='level'>11</stat>
</ms>
<ms aid='354522' cli_name='Hero_Devourer'>
<stat name='nickname'>AbendrothA</stat>
<stat name='level'>12</stat>
</ms>
</match_stats>
</match>
</stats>
</xmlRequest>
I am trying to extract the value of nickName and level using the code below:
XmlNodeList nodeList = doc.SelectNodes("//ms");
List<string> myList = new List<string>();
foreach (XmlNode node in nodeList)
{
XmlNode nodeNickName = node.SelectSingleNode("//stat[#name='nickname']/text()");
mylist.Add(nodeNickName.Value);
}
The problem is that while I can see the node object being updated with next set of data the value returned is always the same as the first nickname.
So nodeNickName.Value is always equal to "lethallynx".
Any ideas?

The // in your //stat[#name='nickname']/text() xpath query selects the root node and searches down from there.
You should replace this with a ./, which takes the search from the current node, as ./stat[#name='nickname']/text()

In your foreach try:
string nickname = node.SelectSingleNode("stat[#name='nickname']").InnerText;
mylist.Add(nickname);

Related

Failing to read value XML attribute in C#

I am trying to read a file produced by another developer. The file looks something like this. I am trying to read in the value for 'ProfileName', but when I look at the object in memory, I see null for the Value (capital V) attribute. The only place I can see the string "GolfLeague-Dual" is in the outerxml attribute, but I would have to parse through a bunch of just to get it.
<?xml version="1.0"?>
<TopNode>
<ProfileSettings>
<ProfileName value="GolfLeague-Dual" />
</ProfileSettings>
</TopNode>
Here is my code to try to read this:
XmlDocument doc = new XmlDocument();
doc.Load(directory + #"\Settings.xml");
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("//ProfileSettings");
foreach (XmlNode node in nodes) {
Console.WriteLine(node["ProfileName"].Value);
}
Your code is trying to get the inner value of the node, not an attribute called value. Try this instead...
foreach (XmlNode node in nodes) {
Console.WriteLine(node["ProfileName"].Attributes["value"].Value);
}
Here's a working dotnetfiddle...
https://dotnetfiddle.net/pmJKbX

XML - where node has same name but different values

I am trying to read an xml file (and later import the data in to a sql data base) which contains employees names address' etc.
The issue I am having is that in the xml the information for the address for the employee the node names are all the same.
<Employee>
<EmployeeDetails>
<Name>
<Ttl>Mr</Ttl>
<Fore>Baxter</Fore>
<Fore>Loki</Fore>
<Sur>Kelly</Sur>
</Name>
<Address>
<Line>Woof Road</Line>
<Line>Woof Lane</Line>
<Line>Woofington</Line>
<Line>London</Line>
</Address>
<BirthDate>1985-09-08</BirthDate>
<Gender>M</Gender>
<PassportNumber>123756rt</PassportNumber>
</EmployeeDetails>
</Employee>
I all other items are fine to extract and I have tried to use Linq to iterate through each "Line" node but it always just gives be the first Line and not the others.
var xAddreesLines = xEmployeeDetails.Descendants("Address").Select(x => new
{
address = (string)x.Element("Line").Value
});
foreach (var item in xAddreesLines)
{
Console.WriteLine(item.address);
}
I need to able to when I'm importing to my sql db that address line is separate variable
eg
var addressline1 = first <line> node
var addressline2 = second <line> node etc etc.
Any advice would be most welcome.
This should give you the expected output:-
var xAddreesLines = xdoc.Descendants("Address")
.Elements("Line")
.Select(x => new { address = (string)x });
You need to simply fetch the Line elements present inside Address node and you can project them. Also note there is no need to call the Value property on node when you use explicit conversion.
You can do it like this:
using System.Xml;
.
.
.
XmlDocument doc = new XmlDocument();
doc.Load("source.xml");
// if you have the xml in a string use doc.LoadXml(stringvar)
XmlNamespaceManager nsmngr = new XmlNamespaceManager(doc.NameTable);
XmlNodeList results = doc.DocumentElement.SelectNodes("child::Employee", nsmngr);
foreach (XmlNode result in results)
{
XmlNode namenode = result.SelectSingleNode("Address");
XmlNodeList types = result.SelectNodes("line");
foreach (XmlNode type in types)
{
Console.WriteLine(type.InnerText);
}
XmlNode fmtaddress = result.SelectSingleNode("formatted_address");
}
Refer to this question for the original source.

Get list of line items from XML

I have an eConnect outgoing document pulled from MSMQ, i need to loop through the line items.
I've tried:
XmlNodeList nodes = root.SelectNodes("/Sales_History_Transaction/eConnect/SO_Hist_Trans/Line");
(and several other attempts) with no success...
Here is the XML, how can i get a collection of Line items from the "Line" nodes so that i can then get the line item details?
<Sales_History_Transaction:root>
<eConnect ACTION="1" Requester_DOCTYPE="Sales_History_Transaction" DBNAME="TWO" TABLENAME="SOP30200" DATE1="2013-05-03T09:24:09.970" SOPNUMBE="999999" SOPTYPE="3">
<SO_Hist_Trans>
<SOPNUMBE>999999</SOPNUMBE>
<SOPTYPE>3</SOPTYPE>
<Line>
<CMPNTSEQ>0</CMPNTSEQ>
<LNITMSEQ>998777</LNITMSEQ>
<ITEMNMBR>0099999</ITEMNMBR>
<ITEMDESC>Laptop</ITEMDESC>
</Line>
<Line>
<CMPNTSEQ>0</CMPNTSEQ>
<LNITMSEQ>777</LNITMSEQ>
<ITEMNMBR>0099</ITEMNMBR>
<ITEMDESC>Desktop</ITEMDESC>
</Line>
<Line>
<CMPNTSEQ>0</CMPNTSEQ>
<LNITMSEQ>679777</LNITMSEQ>
<ITEMNMBR>0569</ITEMNMBR>
<ITEMDESC>Memory</ITEMDESC>
</Line>
</SO_Hist_Trans>
</eConnect>
</Sales_History_Transaction:root>
Your xml is not well-formed.
The root tag seems to consist of an undeclared namespace Sales_History_Transaction and the element name root. Have you missed the line in which Sales_History_Transaction is defined?
Once you have valid xml, it should be as simple (depending on namespaces) as:
var xdoc = XDocument.Parse(yourXml);
var nodes = xdoc.Descendants("Line");
Does this do the trick for your example? Or is this the same you tried that failed?
XmlDocument doc1 = new XmlDocument();
doc1.Load("yoururl"); //I don't know from where you load
XmlElement root = doc1.DocumentElement;
XmlNodeList nodes = root.SelectNodes("/Sales_History_Transaction/eConnect/SO_Hist_Trans/Line");
foreach (XmlNode node in nodes) {
Console.Out.WriteLine(node["CMPNTSEQ"].InnerText);
Console.Out.WriteLine(node["LNITMSEQ"].InnerText);
Console.Out.WriteLine(node["ITEMNMBR"].InnerText);
Console.Out.WriteLine(node["ITEMDESC"].InnerText);
Console.Out.WriteLine("------------------------");
}
Figured it out:
XmlNodeList nodes = xmlDocument.GetElementsByTagName("Line");
foreach (XmlNode node in nodes)
{
string txt = node["ElementName"].InnerText;
}
this enumerates through all the "Line" elements in the XML.

xpath to select elements with last child value

This is my xml file
<profiles>
<profile id='8404'>
<name>john</name>
<name>mark</name>
</profile>
<profile id='8405'>
<name>john</name>
</profile>
</profiles>
and I want to select profiles where last "name" child value= john, the result should contains the profile with id=8405 only
what it the xpath that can evaluate this?
here is my trial:
var filterdFile = profilefileXML.XPathSelectElements("/profiles/profile[name[last()]='john']");
but it doesn't make sense.
Updated:
My trail is correct, there was only a syntax error in it. Thanks all
You can apply multiple indexing operations with successive [...]:
var doc = XDocument.Parse(xml); //the xml from your question
var node = doc.XPathSelectElement("/profiles/profile[name='john'][last()]");
Console.WriteLine(node.Attribute("id").Value); //outputs 8405
This will return the last profile element that contains the element name with a value of john.
If you on the other hand want to return all elements which last name element has a value of john, your XPath should work already:
var nodes = doc.XPathSelectElements("/profiles/profile[name[last()]='john']");
foreach (var node in nodes)
{
Console.WriteLine(node.Attribute("id").Value);
}
You can also try LINQ
XDocument xDoc = XDocument.Load("data.xml");
var matches = xDoc.Descendants("profile")
.Where(profile => XElement.Parse(profile.LastNode.ToString()).Value == "john");
And you can access the xml data with a foreach
foreach(XElement xEle in lastEle)
{
var xAttribute = xEle.Attribute("id");
if (xAttribute != null)
{
var id = xAttribute.Value;
}
var lastName = XElement.Parse(xEle.LastNode.ToString()).Value;
}

looping through items using xPath

I am tryng to loop through an xml doc and I am still getting the first element in the second iteration, not sure what I am missing. Can anyone help? Pretty new with Xpath
string file = HttpContext.Current.Server.MapPath("~/XML/Locations.xml");
Dictionary<string, Location> locationCollection = new Dictionary<string, Location>();
XPathDocument xDocument = new XPathDocument(file);
XPathNavigator xPathNavigator = xDocument.CreateNavigator();
foreach (XPathNavigator node in xPathNavigator.Select("//locations/*"))
{
string value = node.SelectSingleNode("/locations/location/cell").Value;
}
<?xml version="1.0" encoding="utf-8" ?>
<locations>
<location>
<locationName>Glendale</locationName>
<street>3717 San Fernando Road</street>
<city>Glendale</city>
<state>CA</state>
<zipcode>91204</zipcode>
<generalManager>DJ Eldon</generalManager>
<phone>(818) 552‐6246</phone>
<tollFree>(888) 600‐6011</tollFree>
<fax>(818) 552‐6248</fax>
<cell>(347) 834‐2249</cell>
<counterEmail>BUR#Eaglerider.com</counterEmail>
<directEmail>DJ#Eaglerider.com</directEmail>
</location>
<location>
<locationName>Chicago</locationName>
<street>1301 S. Harlem Ave.</street>
<city>Chicago</city>
<state>IL</state>
<zipcode>60402</zipcode>
<generalManager>Dave Schnulle</generalManager>
<phone>(708) 749‐1500</phone>
<tollFree>(888) 966‐1500</tollFree>
<fax>(818) 552‐6248</fax>
<cell>(708) 749‐3800</cell>
<counterEmail>ORD#Eaglerider.com</counterEmail>
<directEmail>Dave#Eaglerider.com</directEmail>
</location>
</locations>
You're effectively ignoring the value of node by using a leading slash to get back to the document root. Try this instead:
// This assumes that there are only location nodes under locations;
// You may want to use //locations/location instead
foreach (XPathNavigator node in xPathNavigator.Select("//locations/*"))
{
string value = node.SelectSingleNode("cell").Value;
// Use value
}
Having said that, is there any reason you're not doing it in a single XPath query?
// Name changed to avoid scrolling :)
foreach (XPathNavigator node in navigator.Select("//locations/location/cell"))
{
string value = node.Value;
// Use value
}
Try the following:
XPathNodeIterator ni = xPathNavigator.Select("//locations/*");
while (ni.MoveNext())
{
string value = ni.Current.Value);
}
Just a quick blurt, hope it helps you.
you should do:
string value = node.SelectSingleNode("./cell").Value;
When you do xPathNavigator.Select("//locations/*")), you're already inside /locations/location so you need to move just one element down the node, cell in your example.

Categories

Resources