I have the following XML:
<Items>
<textBoxCenterName>Denver Dispatch</textBoxCenterName>
<textBoxContactFirstName>Eric</textBoxContactFirstName>
<servicedUnits>
<unit>CO-ADX (Adams County)</unit>
<unit>CO-AFQ (Air Force Academy)</unit>
<unit>CO-ALDS (CSFS-Alamosa District)</unit>
<unit>CO-ALX (Alamosa County)</unit>
</servicedUnits>
</Items>
I am using the following code to pull in the servicedUnits as listBox items:
XElement element = XElement.Load("FCAT-Settings.xml");
foreach (XElement item in element.Elements("servicedUnits"))
listBoxServicedUnits.Items.Add(item.Value);
The items are coming in as one long string concatenated, like so:
CO-ADX (Adams County)CO-AFQ (Air Force Academy)CO-ALDS (CSFS-Alamosa District)CO-ALX (Alamosa County)
So I end up with one item in the listbox with all these servicedUnits strung together.
How can I solve this? Any ideas are welcome.
The problem is with the selection. element.Elements("servicedUnits") returns the list of servicedUnits elements (Only 1 in the XML), and what you wanted is the list of elements INSIDE that element, for example element.Elements("servicedUnits").Elements().
You might try getting child nodes:
foreach (XElement item in element.Elements("servicedUnits").Elements())
You are attempting to iterate over a single element (servicedUnits) instead of iterating over the collection of unit elements that you intend to. The output of your current implementation is simply giving you the total Value of your servicedUnits element, which is the value of each of its child elements.
To get what you seemingly intend to have, you need to iterate over your unit elements, like this:
foreach(XElement item in element.Element("servicedUnits").Elements("unit"))
{
//add to listbox, whatever
}
This sample implementation assumes you only have a single servicedUnits element, FYI.
An easy way to do this is by using the Descendents method on an XDocument:
var xdoc = XDocument.Load(#"FCAT-Settings.xml");
var units = xdoc
.Descendants("servicedUnits")
.SelectMany(u => u.Descendants("unit").Select(x => x.Value));
foreach (var unit in units)
listBoxServicedUnits.Items.Add(unit);
This also ensures you will only get the values for the "unit" nodes.
Related
Context: Using the HTMLAgilityPack library, im looping a HtmlNodeCollection, printing the HTML of the node gives me the data that I need, but when im selecting nodes inside the html, all of them gives me the result of the first item I selected nodes in.
Writing the nodes html as node.InnerHtml gives me the unique htmls of them, all correct, but when I do SelectSingleNode, all of them give me the same data.
Due to the project, I cannot disclose the website. What I can say is that theres 17 nodes, all of them are a div with the class k-user-item. All Items are unique, meaning they all are different.
Thanks for the help!
Code:
var nodes = w.DocumentNode.SelectNodes("//div[contains(#class, 'k-user-item')]");
List<Sales> saleList = new List<Sales>();
foreach (HtmlNode node in nodes)
{
//This line prints correct html, selecting single nodes gives me always the same data of the first item from the loop.
//Debug.WriteLine(node.InnerHtml);
string payout = node.SelectSingleNode("//*[#class=\"k-item--buy-date\"]").InnerText;
string size = node.SelectSingleNode("//*[#class=\"k-panel-title\"]").SelectNodes("//span")[1].InnerText;
var trNodes = node.SelectNodes("//tr");
string status = trNodes[1].SelectSingleNode("//b").InnerText;
string orderId = trNodes[2].SelectNodes("//td")[1].SelectSingleNode("//span").InnerHtml;
string sellDate = node.SelectSingleNode("//*[#class=\"k-panel-heading\"]").SelectNodes("//small")[1].InnerHtml;
}
This issue was solved by adding to the XPath a "." on to the start.
Not adding the dot onto the XPath means that the node will search in the whole document and not just the exact node html.
I have XML file and I have parse it into XDocument.
I need to get all tags with name <Entity>, but there is a one problem.
Tag <Entity> contains two more Entity tags as his children. (among with many others tag as well).
When I do this:
var entity= xmlDoc.Descendants().Where(x => x.Name.LocalName == "Entity");
I get them all of course.
Is there a way to tell: Get me all Entity tags, but not Entity tag that is child of an Entity tag?
Structure looks like this:
<Entity> --> I need to get this one
<SomeTags>Value</SomeTags>
<First>
<Entity>Value</Entity> --> Skip this one
</First>
<Entity>Value<Entity> --> Skip this one as well
</Entity>
You could use the following:
private String path= #"C:\Temp\xml.xml"; //YOur XML path
public string getValue(string Name)
{
try
{
doc = XDocument.Load(path);
var dada = (from c in doc.Descendants(Name)
where c.Parent.Name!=Name
select c).First();
return dada.Value;
}
catch (Exception)
{
global::System.Windows.Forms.MessageBox.Show("There was a problem with the XML");
return "";
}
}
Descendants gets all child elements recursively. Assuming the elements you want are all at the same depth, you need to find their parent element and query using Elements instead - this will only get the immediate children.
doc.Descendants("parent")
.Elements("Entity");
If that doesn't work for your structure, you could also literally query as you've suggested - find all those Entity elements that don't have any parent Entity elements:
doc.Descendants("Entity")
.Where(x => !x.Ancestors("Entity").Any());
Building on #CharlesMager's second example, this should be the correct syntax:
doc.Descendants("Entity").Where(x => !x.Ancestors("Entity").Any());
btw: one of your Entities isn't closed
Example on dotNetFiddle
So lets say I have a XElement that looks something like this
<Root>
<ProductOne>
<Size>Large</Size>
<Height>2</Height>
</ProductOne>
<ProductTwo>
<Size>Small</Size>
<Type>Bar</Type>
</ProductOne>
<ProductThree>
<Size>Small</Size>
<Type>Crate</Type>
<Color>Blue</Color>
</ProductOne>
<SomeOtherStuff>
<OtherThing>CrazyData</OtherThing>
</SomeOtherStuff>
</Root>
I want to query this data and get a IEnumerable string of the child values (I.E. Size, Type, Color, and a lot of other possuble attributes) of anything that is in a element with the word "Product" in it.
So My resulting list would look like
Large
2
Small
Bar
Small
Crate
Blue
Could someone tell me how to construct such a query using LINQ?
First, you have a lot of typos with your xml. Here is the correct version:
var xml = #"
<Root>
<ProductOne>
<Size>Large</Size>
<Height>2</Height>
</ProductOne>
<ProductTwo>
<Size>Small</Size>
<Type>Bar</Type>
</ProductTwo>
<ProductThree>
<Size>Small</Size>
<Type>Crate</Type>
<Color>Blue</Color>
</ProductThree>
<SomeOtherStuff>
<OtherThing>CrazyData</OtherThing>
</SomeOtherStuff>
</Root>";
Now, here is some linq magic you can do to get the values you want.
var list = XElement.Parse(xml) //parses the xml as an XElement
.Elements() //gets all elements under the "root" node
.Where(x => x.Name.LocalName.StartsWith("Product")) // only selects elements that
// start with "product"
.SelectMany(x => x.Elements()) // inside of each of the "product" nodes, select
// all the inner nodes and flatten the results
// into a single list
.Select(x => x.Value) //select the node's inner text
.ToList(); //to list (optional)
This will give you back your wanted list as a List<string>.
Large
2
Small
Bar
Small
Crate
Blue
I have a large, messy XML file and I want to retrieve ALL elements of the same name ("Item" for the sake of this post) from it, then be able to retrieve data from each element's children.
So far I have returned a list of every element called "Item" using this code, which just displays the namespace url and "Item" in p tags:
XDocument doc = XDocument.Load(#"C:\inetpub\wwwroot\mysite\myxml.xml");
XNamespace ns = "http://www.mynamespace.com";
var nodes = doc.Descendants().Elements(ns + "Item").Select(d => d.Name).ToList();
foreach(var x in nodes){
<p>#x</p>
}
However, by amending the code with the following, I can't retrieve any data of it's children and I get the error 'System.Xml.Linq.XName' does not contain a definition for 'Descendants':
foreach(var x in nodes){
<p>#x.Descendants().Element("Name")</p>
}
Here is a very basic version of my XML file:
<Item>
<Name>Item 1</Name>
<Type>Type 1</Type>
</Item>
I want to be able to search each 'Item' element for a 'Name' element and return the value. Can anyone see where I'm going wrong?
This is the problem:
.Select(d => d.Name)
You're explicitly selecting the names of the elements. If you want the actual elements (which I think you do), just get rid of that call:
var nodes = doc.Descendants().Elements(ns + "Item").ToList();
You could also get rid of the ToList() unless you need the query to be materialized eagerly.
I need to get value of child node from XDocument using linq
<root>
<Cust>
<ACTNumber>1234</ACTNumber>
<Address>
<Street></Street>
<City>123 Main street</City>
<State>AL</State>
</Address>
</Cust>
</root>
I tried this: xDocTest.Root.Elements("Cust").Elements("ACTNumber")
If I try Address instead of ACTNumber then it works. But its not giving the child node value.
If there's only one Cust element and only one ACTNumber element, then it's easy:
string actNumber = doc.Root.Element("Cust").Element("ACTNumber").Value;
Or to get it as a long:
long actNumber = (long) doc.Root.Element("Cust").Element("ACTNumber");
Use this:
xDocTest.Root.Element("Cust").Element("Adress").Element("City").Value
If you use Elements (note the plural) it gives u an IEnumerable, this would be used like this:
XML
<Father>
<Child>Hello</Child>
<Child>World!</Child>
</Father>
C#
foreach(var childElement in Root.Elements("Child")) Console.WriteLine(childElement.Value);
Or to take your example:
foreach(var child in xdoc.Root.Element("Cust").Element("Address").Elements())
Console.WriteLine(string.Format("{0} : {1}", child.Name, child.Value);
Im not sure how Element behaves if you have multiple Elements of the same name. So you might want to use Elements and Inerate over all occurences.
And in Linq
If there is more than one Customer...
var result = from cust in xdoc.Root.Elements("Cust")
where cust.Elements("ACTNumber").Any() // This is to make sure there
// is an element called ACTNumber
// otherwise .Value would create
// Nullrefexception.
select child.Element("ACTNumber").Value;