I have the following XML Document being loaded into C# Silverlight:
<parent>
<son name="Jim">
<grandson>Billy</grandson>
<granddaughter>Sue</granddaughter>
</son>
<daughter name="Sally">
</daughter>
</parent>
I'd like to do a LINQ query so that I query parent and get a list of "son" and "daughter" nodes only. When I get to a node of type "son", I want to do another query for its own children.
I've tried this:
IEnumerable<XElement> Children =
from childNode in parents.Descendants()
select (XElement)childNode ;
foreach(XElement childNode in Children){
// other code
}
but that gives me everything (son, daughter, grandson, granddaughter).
What I'd like to do is something like this:
foreach(XElement childNode in Children){
switch(childNode.Name.ToString()){
case "son":
// look for "grandson" and "granddaughter" as children of "son" now
break;
case "daughter":
// don't look for anything
break;
}
}
So Basically, I only want the first level of children returned in the query, and I will query for the rest later on.
I'm not sure if it should be done in the original LINQ query, in the foreach condition, or what. I don't have control over the XML document format so I can't change it to something better. Thanks!
If I understand your question correctly you need to use the Elements function:
var sons_qry = from son in parents.Elements()
select son;
var grandsons_qry = from grandson in sons_qry.Descendants()
select grandson;
Related
Per this XML, please note that BBB exists on two node levels.
<?xml version="1.0" encoding="utf-8"?>
<AAA>
<BBB>
<BBB>ONE</BBB>
<CCC>1</CCC>
<DDD>2</DDD>
<EEE>3</EEE>
</BBB>
<BBB>
<BBB>TWO</BBB>
<CCC>4</CCC>
<DDD>5</DDD>
<EEE>6</EEE>
</BBB>
<BBB>
<BBB>THREE</BBB>
<CCC>7</CCC>
<DDD>8</DDD>
<EEE>9</EEE>
</BBB>
</AAA>
I want to derive a collection of top level BBB's and extract them to their own file, with a file name based on the inner BBB.
My code is this:
XDocument xdoc = XDocument.Load(sourceFile);
var lv1s = from lv1 in xdoc.Descendants("AAA") select lv1;
var lv2s = from lv2 in xdoc.Descendants("BBB") select lv2;
foreach (var lv2 in lv2s)
{
var name = lv2.Element("BBB").Value;
lv2.Save(#"c:\temp\" + name + ".xml");
}
Problem is, LVL2 is picking up both the parent and descendants BBB.
Can't seem to find a method that effectively filters out descendants.
For example I thought this was the key, but it yielded no results:
var lv2s = from lv2 in xdoc.Elements("BBB") select lv2;
Hoping you can provide me a ways to deal with the problem.
-------------------- EDIT --------------------
Okay I see what I did wrong. A typo.
LVL2 should have leveraged LVL1, like this:
var lv2s = from lv2 in lv1s.Elements("BBB") select lv2;
That said, octavioccl's approach was much better than the bloated solution I came up with:
var parentBbbs =xdoc.Element("AAA").Elements("BBB");
You need to start getting the root element and then select the parent BBBs using Elements method:
var parentBbbs =xdoc.Element("AAA").Elements("BBB");
Just document.Root.Elements() should work.
Basically Descendants() recurses, whereas Elements() only gets direct children.
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.
For an application I am working on, I have to display data from an XML File. There's a few transformations being done, but eventually the end result will be displayed in a treeview. When a user then clicks on a node, I want to pop up the details in a listview.
When no node has been selected, I basically use LINQ to grab the details of the first item I encounter.
Here's a simplified version of my XML
<root>
<parent label="parent1">
<child label="child1">
<element1>data</element1>
<element2>data</element2>
...
</child>
<child label="child2">
<element1>data</element1>
<element2>data</element2>
...
</child>
</parent>
</root>
And here's the code used to grab it (After selecting the parent-node that the treeview has been set to by means of an XPAthSelectStatement):
protected void listsSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
XElement rootElement = XElement.Load(MapPath(TreeSource.DataFile));
rootElement = rootElement.XPathSelectElement("//parent[#label='parent1']");
XElement parentElement;
parentElement = rootElement;
var query = (from itemElement in parentElement.Descendants("child")
select new
{
varElement1 = itemElement.Element("element1").Value,
varElement2 = itemElement.Element("element2").Value,
...
}).Take(1);
e.result = Query;
}
This works a treat, and I can read out the varElement1 and varElement2 values from there. However, when I try and implement a similar mechanism for when the user actually did select a node, I seem to run into a wall.
My approach was to use another XPatchSelectStatement to get to the actual node:
parentElement = rootElement.XPathSelectElement("//child[#label='" + tvwChildren.SelectedNode.Text + "']");
But I am kind of stumped on how to now get a proper LINQ query built up to read in all elements nested under the child node. I tried using parentElement.Elements(), but that was yielding an error. I also looked at using Nodes(), but with similar results.
I suppose I could use a foreach loop to access the nodes, but then I'm not sure how to get the results into a LINQ query so I can return the same e.Result = query back.
I'm fairly new to LINQ, as you might have guessed, so any hints would be very much appreciated.
Here's the query that will give you the child element (given that there is only one child element with the specified label):
var childElement = rootNode.Descendants("child")
.Single(e=>e.Attribute("label").Value == "child1");
If you have more than one child elements with label="child1" but those elements are under different parent elements you can use the same approach to get first the parent element and then the child element.
Having the above, you can use this query to get all element nodes under the child node:
var elements = childElement.Descendants().Select(e=>e.Value);
I think data binding is much easier in this case.
XDocument doc = XDocument.Load(filePath);
if (doc.Root == null)
{
throw new ApplicationException("invalid data");
}
tvwChildren.Source=doc;
But if you want in this way hope following one helps(not the exact solution)
XElement root = XElement.Load("Employees.xml");
TreeNode rootNode = new TreeNode(root.Name.LocalName);
treeView1.Nodes.Add(rootNode);
foreach(XElement employee in root.Elements())
{
TreeNode employeeNode = new TreeNode("Employee ID :" + employee.Attribute("employeeid").Value);
rootNode.Nodes.Add(employeeNode);
if (employee.HasElements)
{
foreach(XElement employeechild in employee.Descendants())
{
TreeNode childNode = new TreeNode(employeechild.Value);
employeeNode.Nodes.Add(childNode);
}
}
}
And you can try Resharper tool for create better linq statements. It shows possible ones and you can easily convert each for,foreach loops into linq statements.
I'm not entirely sure I understand what you're trying to do, but it sounds like it could be this:
var data =
from p in xml.Root.Elements("parent")
where p.Attribute("label").Value == "parent1"
from c in p.Elements("child")
where c.Attribute("label").Value == "child2"
from d in c.Elements()
select d.Value;
Let me know if that helps.
Using this Xml library you can write your XPath like:
XElement child = rootElement.XPathElement(
"//parent[#label={0}]/child[#label={1}]", "parent1", "child2");
I have code which parses XML that looks like this:
<custom_fields>
<custom_field>
<column_name>foo</column_name>
<column_value>0</column_value>
<description>Submitted</description>
<data_type>BOOLEAN</data_type>
<length>0</length>
<decimal>0</decimal>
</custom_field>
<custom_field>
<column_name>bar</column_name>
<column_value>0</column_value>
<description>Validated</description>
<data_type>BOOLEAN</data_type>
<length>0</length>
<decimal>0</decimal>
</custom_field>
</custom_fields>
... more <custom_field> elements...
I want to find the element called custom_field which has a child element called column_name with a certain value (for example bar), and then find that child's sibling called column_value and get its value. Right now I use XPath on an XMlDocument to do this:
string path = "//custom_fields/custom_field[column_name='" + key + "']";
XmlNode xNode = doc.SelectSingleNode(path);
if (xNode != null)
{
XmlNode v = xNode.SelectSingleNode("column_value");
val.SetValue(v.InnerText);
}
Where key is the name of the field I am looking for.
But I want to do this using the new LINQ to XML syntax on an XDocument. My thinking is that I will move much of my old-style XPath parsing to the LINQ methods. Maybe it's not a good idea, but this is a case where if I can get it to work, then I believe I will have a much better understanding of LINQ in general, and will be able to clean up a lot of complex code.
You can always use XPath within LINQ to XML. Just include the System.Xml.XPath namespace.
var xpath = $"//custom_fields/custom_field[column_name='{key}']/column_value";
var columnValue = doc.XPathSelectElement(xpath);
if (columnValue != null)
{
val.SetValue((int)columnValue);
}
Otherwise for the equivalent LINQ to XML query:
var columnValue = doc.Descendants("custom_fields")
.Elements("custom_field")
.Where(cf => (string)cf.Element("column_name") == key) // assuming `key` is a string
.Elements("column_value")
.SingleOrDefault();
Your XQuery expression
//custom_fields/custom_field[column_name='key']
selects all custom_field elements in custom_fields elements where the value of the column_key child element equals "key". You expect a single element to be returned and select the value of the column_value child element.
You can express this using LINQ to XML as follows:
var doc = XDocument.Load(...);
var query = from fields in doc.Descendants("custom_fields")
from field in fields.Elements("custom_field")
where (string)field.Element("column_name") == "key"
select (int)field.Element("column_value");
int result = query.Single();
I want to find the element called
custom_field which has a child element
called column_name with a certain
value (for example "bar", and then
find that child's sibling called
column_value and get its value.
Use:
/custom_fields/custom_field[column_name = 'bar']/column_value
I have this XML file:
<?xml version="1.0" encoding="utf-8"?>
<aBase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>
<a>
<b>some data</b>
<c>other data</c>
</a>
<a>
<b>some data</b>
<c>other data</c>
</a>
</a>
</aBase>
I want to get all data from <b> nodes using LINQ to XML.
I have tried something like this:
var query = from c in xmlData.Descendants("b")
select c.Value;
but it doesn't work.
How can I do this?
Also, what is the difference between Descendents, Elements and Nodes?
You can write this query to get all information from inner a nodes:
var query = from e in doc.Root.Elements( "a" ).Elements( "a" )
select new { B = e.Element( "b" ).Value, C = e.Element( "c" ).Value };
But this is just a query. To execute it and work with results write this code:
foreach ( var e in query )
{
// Do something with results... For example, write to console:
Console.WriteLine( "B: " + e.B + ", C: " + e.C );
}
Node is the one element of the XML tree. aBase element, all the a, b and c elements - the nodes of the XML document.
Elements is just the child elements of selected node (in one level of hierarchy). For example for the aBase node the only child element is the outer a element, not the inner a, b or c element.
Descendants is all the elements that are child or descendants to the current node (in all levels of hierarchy). All elements are the descendants to the aBase element because it is the main element of the document.
xmlData.Descendants("b") will give you a collection of XElements with XName equal to "b". You can then iteration over this collection to get every value:
var data = xmlData.Descendants("b");
foreach (XElement b in data)
{
string value = b.Value;
// do something with value here
}
Descendants will give you any Element that is a descendant, not only the direct children, but also the children of them. So in your case xmlData.Root.Descendants("b") will return a collection over every B element in the file. Elements does the same thing but only for direct descendants and Nodes does the same thing but includes comments and textNodes.
Here you go:
XDocument doc = XDocument.Parse(xml);
XNamespace ns = doc.Root.Name.Namespace;
var query = doc.Root.Elements(ns + "a").Elements(ns + "a").Elements().Select(el => el.Value);
Please note that you don't even need neither the Descendants method nor iteration through descendants. Elements without parameters returns direct descendants, and Select method extracts values of descendants. And don't forget about xml namespace or elements names could not be found. Without namespace you're trying to extract different elements, not the elements in your xml.
As for difference between different methods:
Nodes() returns IEnumerable<XNode>
Elements() returns IEnumerable<XElement>
Descendants() returns all descendant elements (including all deeply nested elements in opposite to Nodes and Elements return only 1st level child elements of calling element.
var query = from c in xmlData.Descendants("b").First()
select c.Value;