looping through items using xPath - c#

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.

Related

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.

Tricky to read XML from web service C#

I have looked everywhere, and cannot find anything to help me.
I am writing a program that connects to a webservice and then the webservice sends an XML response. After the response is received I have to retrieve certain values from it, but this is where it gets tricky
Here is a snippet of the returned XML:
<?xml version="1.0"?>
<MobilePortalSellingCategoriesHierarchy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Response xmlns="http://blabla.com/service/">Successful</Response>
<ResponseNumber xmlns="http://blabla.com/service/">0</ResponseNumber>
<SellingCategoriesHierarchy xmlns="http://tym2sell.com/PortalService/">
<Response>Successful</Response>
<ResponseNumber>0</ResponseNumber>
<SellingCategories>
<PortalSellingCategory>
<SellingCategoryId xsi:nil="true" />
<SellingCategoryName>category1</SellingCategoryName>
<DeliveryMethod />
<DeliveryMethodNumber>0</DeliveryMethodNumber>
<SellingCategories>
<PortalSellingCategory>
<SellingCategoryId xsi:nil="true" />
<SellingCategoryName>category1_Item</SellingCategoryName>
<DeliveryMethod />
<DeliveryMethodNumber>0</DeliveryMethodNumber>
<SellingCategories>
<PortalSellingCategory>
<SellingCategoryId>2</SellingCategoryId>
<SellingCategoryName>Item2</SellingCategoryName>
<DeliveryMethod>Display</DeliveryMethod>
<DeliveryMethodNumber>1</DeliveryMethodNumber>
<VoucherValue>0.00</VoucherValue>
<IsVariablePrice>true</IsVariablePrice>
<MinimumVoucherValue>1.00</MinimumVoucherValue>
<MaximumVoucherValue>1000.00</MaximumVoucherValue>
<VoucherValueIncrement>1.00</VoucherValueIncrement>
<AdditionalInputItems>
<PortalAdditionalInputItem>
<InputItemId>-1</InputItemId>
<Label>Value:</Label>
<IsNumericOnly>true</IsNumericOnly>
<MaximumLength>7</MaximumLength>
<Hidden>false</Hidden>
</PortalAdditionalInputItem>
<PortalAdditionalInputItem>
<InputItemId>4</InputItemId>
<Label>Mobile Number</Label>
<IsNumericOnly>true</IsNumericOnly>
<MaximumLength>15</MaximumLength>
<Hidden>false</Hidden>
</PortalAdditionalInputItem>
</AdditionalInputItems>
<TwoStep>false</TwoStep>
<SelectedIcon>SamplePicture</SelectedIcon>
<UnSelectedIcon>SamplePicture</UnSelectedIcon>
This repeats from the SellingCategories node just under Response for a couple of times.
Here is a Snippet of my code where I get the XML as string.
XmlDocument xml = new XmlDocument();
xml.LoadXml(receivedData);
XmlNodeList xnList = xml.SelectNodes("/MobilePortalSellingCategoriesHierarchy");
foreach (XmlNode xn in xnList)
{
string sellingCategoryName = xn["SellingCategoryName"].InnerText;
string SelectedIcon = xn["SelectedIcon"].InnerText;
string UnSelectedIcon = xn["UnSelectedIcon"].InnerText;
richTextBox1.AppendText(string.Format("Name: {0} {1} {2}", sellingCategoryName, SelectedIcon, UnSelectedIcon));
}
I have tried changing xml.SelectNodes("/MobilePortalSellingCategoriesHierarchy");
to
xml.SelectNodes("/MobilePortalSellingCategoriesHierarchy/SellingCategoriesHierarchy/SellingCategories/PortalSellingCategory");
I need to select each SellingCategoryName and list the SellingCategoryName(s) and all the other items underneath it.
I was hoping to get something in the lines of:
Category1
Category1_Item
Item2
SamplePicture
Sample Picture
Mine only reads the First Node and then returns "Successful" to me.
I havve also tried:
XElement root = XElement.Load("FilePath");
var sellingCategoryNames = from PortalSellingCategory in root.Elements("MobilePortalSellingCategoriesHierarchy")
where (string)PortalSellingCategory.Element("SellingCategoriesHierarchy").Element("SellingCategories").Element("PortalSellingCategory") != ""
select PortalSellingCategory;
foreach (var xEle in sellingCategoryNames)
{
richTextBox1.Text = (string)xEle;
}
Any help would be greatly appreciated.
What you are doing with
xml.SelectNodes("/MobilePortalSellingCategoriesHierarchy");
is selecting your root node, which is just one. Thats why you only get one item in your list back. Is the hierarchy important? I can see that PortalSellingCategory can also be inside another PortalSellingCategory. If not maybe you could try the following select:
xml.SelectNodes("//PortalSellingCategory");
This will search for every node named "PortalSellingCategory" in your response, no mather where in the hierarchy it is.
EDIT:
And yes, you should look out for the namespaces, sry for didnt seeing that.
If you like geeting all the nodes with XPath you must create a new NamespaceManager and at it to your selectNodes call:
XmlDocument xml = new XmlDocument();
xml.LoadXml(data);
XmlNamespaceManager ns = new XmlNamespaceManager(xml.NameTable);
ns.AddNamespace("ns", "http://tym2sell.com/PortalService/");
XmlNodeList xnList = xml.SelectNodes("//ns:PortalSellingCategory", ns);
I would use XElement instead of XMLDocument, and then use Linq to query or pick the elmements like: XElement xContact = ....
int contactno = (int?)xContact.Element("command").Element("contactperson").Attribute("contactpersonid") ?? -1;
if (xContact.Element("command").Element("contactperson").Element("name").Element("firstname") != null)
console.writeline(xContact.Element("command").Element("contactperson").Element("name").Element("firstname").Value);
var doc= new XmlDocument();
doc.Load("FilePath");
var nodeList = xml.GetElementsByTagName("PortalSellingCategory");
Hi,
It returns the collection of nodes, and you just have to query it to get needed informations.
Feel free to ask help if needed.
Dimitri.

Parsing XML for nodes, and then child nodes

My XML looks like this:
<data>
<location ....>
<stores>
<store ....></store>
<store ... ></store>
</stores>
</location>
</data>
I want to loop through all the location elements, and then loop through all the stores.
I also want access to the attributes of both location and store elements.
I have loaded the xml document:
var doc = new XmlDocument();
doc.LoadXml(myXmlAsString);
Let's say this is your sample XML
<data>
<location sample="something">
<stores>
<store atrb="1">store1</store>
<store atrb="2">store2</store>
</stores>
</location>
<location>
<stores>....</stores>
</location>
</data>
Then using XDocument of System.Xml.Linq, you access all the locations, stores and their attributes like this
var xmlString = File.ReadAllText(#"C:\YourDirectory\YourFile.xml");
XDocument geneva = XDocument.Parse(xmlString);
var locations = geneva.Descendants("location");
foreach (var location in locations)
{
var sampleAttribute = location.Attribute("sample").Value;
var stores = location.Descendants("store");
foreach (var store in stores)
{
var value = store.Value;
var atrbValue = store.Attribute("atrb").Value;
}
}
If you dont want to use XmlSerializer to get the typed data you can get nodes by criteria with SelectSingleNode or SelectNodes methods. This will give you ability to iterate through location elemets.
I would suggest creating classes to deserialize to because its easier to work with.
Loop through locations:
XmlNodeList xnList = doc.SelectNodes("/data/location");
foreach(XmlNode node in xnList)
{
//your code goes here..
}
Loop through all stores:
XmlNodeList xnList = doc.SelectNodes("/data/location/stores");
foreach(XmlNode node in xnList)
{
//your code here..
}

Get certain xml node and save the value

Considering the following XML:
<Stations>
<Station>
<Code>HT</Code>
<Type>123</Type>
<Names>
<Short>H'bosch</Short>
<Middle>Den Bosch</Middle>
<Long>'s-Hertogenbosch</Long>
</Names>
<Country>NL</Country>
</Station>
</Stations>
There are multiple nodes. I need the value of each node.
I've got the XML from a webpage (http://webservices.ns.nl/ns-api-stations-v2)
Login (--) Pass (--)
Currently i take the XML as a string and parse it to a XDocument.
var xml = XDocument.Parse(xmlString);
foreach (var e in xml.Elements("Long"))
{
var stationName = e.ToString();
}
You can retrieve "Station" nodes using XPath, then get each subsequent child node using more XPath. This example isn't using Linq, which it looks like you possibly are trying to do from your question, but here it is:
XmlDocument xml = new XmlDocument();
xml.Load(xmlStream);
XmlNodeList stations = xml.SelectNodes("//Station");
foreach (XmlNode station in stations)
{
var code = station.SelectSingleNode("Code").InnerXml;
var type = station.SelectSingleNode("Type").InnerXml;
var longName = station.SelectSingleNode("Names/Long").InnerXml;
var blah = "you should get the point by now";
}
NOTE: If your xmlStream variable is a String, rather than a Stream, use xml.LoadXml(xmlStream); for line 2, instead of xml.Load(xmlStream). If this is the case, I would also encourage you to name your variable to be more accurately descriptive of the object you're working with (aka. xmlString).
This will give you all the values of "Long" for every Station element.
var xml = XDocument.Parse(xmlStream);
var longStationNames = xml.Elements("Long").Select(e => e.Value);

Iterate through XmlNodeList, value is always the same

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);

Categories

Resources