What is the difference between Linq to XML Descendants and Elements
Elements
finds only those elements that are direct descendents, i.e. immediate children.
Descendants
finds children at any level, i.e. children, grand-children, etc...
Here is an example demonstrating the difference:
<?xml version="1.0" encoding="utf-8" ?>
<foo>
<bar>Test 1</bar>
<baz>
<bar>Test 2</bar>
</baz>
<bar>Test 3</bar>
</foo>
Code:
XDocument doc = XDocument.Load("input.xml");
XElement root = doc.Root;
foreach (XElement e in root.Elements("bar"))
{
Console.WriteLine("Elements : " + e.Value);
}
foreach (XElement e in root.Descendants("bar"))
{
Console.WriteLine("Descendants : " + e.Value);
}
Result:
Elements : Test 1
Elements : Test 3
Descendants : Test 1
Descendants : Test 2
Descendants : Test 3
If you know that the elements you want are immediate children then you will get better performance if you use Elements
instead of Descendants
.
XDocument.Descendants() versus DescendantNodes()
Descendants returns only elements. DescendantNodes returns all nodes (including XComments, XText, XDocumentType etc).
Consider following xml to see the difference:
<root>
<!-- comment -->
<foo>
<bar value="42"/>Oops!
</foo>
</root>
Descendants
will return 3 elements (root
, foo
, bar
). DescendantNodes
will return these three elements, and 2 other nodes - text and comment.
What is the difference XElement Nodes() vs Elements()?
The reason is simple: XNode
is a base (abstract) class for all xml "parts", and XElement
is just one such part (so XElement
is subclass of XNode
). Consider this code:
XDocument doc = XDocument.Parse("<root><el1 />some text<!-- comment --></root>");
foreach (var node in doc.Root.Nodes()) {
Console.WriteLine(node);
}
foreach (var element in doc.Root.Elements()) {
Console.WriteLine(element);
}
Second loop (over Elements()
) will only return one item: <el />
First loop however will return also text node (some text
) and comment node (<!-- comment -->
), so you see the difference.
You can see what other descendants of XNode
there are in documentaiton of XNode class.
LINQ to XML Elements() and Descendants() yielded no results even the elements do exist
You've got a non-standard namespace there:
<RMAStateAcknowledgement ... xmlns="http://XX.ITTS.OA30/digitaldistribution/2012/05">
Therefore all of your elements live in that namespace, and you need to use that when querying them.
XNamespace ns = "http://XX.ITTS.OA30/digitaldistribution/2012/05";
var ele = doc.Descendants(ns + "ReturnRequests");
If you want to use .Element
(which only searches down a single level), you need to be querying the root element of the document ("RMAStateAcknowledgement"), not the document itself:
XNamespace ns = "http://XX.ITTS.OA30/digitaldistribution/2012/05";
var ele = doc.Root.Element(ns + "ReturnRequests");
Linq to XML elements and descendants in the same search
If your document structure always has to contain those nodes your query (and accessing SourceTable
) is fine. It's fairly obvious of what's going on given reader knows how XML looks like.
However, if you want more top-down approach (which might seem more natural and easier to grasp), you can always query Table
node first and store FieldMapping
in a variable, but I wouldn't say it has any advantages over your approach:
var nodes = (from table in doc.Descendants("Table")
let fieldMapping = table.Element("FieldMapping")
select new
{
SourceTable = (string)table.Element("SourceTable").Value,
RecordID = (string)fieldMapping.Element("RecordID").Value,
StartYear = (string)fieldMapping.Element("StartYear").Value,
EndYear = (string)fieldMapping.Element("EndYear").Value,
LastName = (string)fieldMapping.Element("LastName").Value,
Title = (string)fieldMapping.Element("Title").Value,
Subject = (string)fieldMapping.Element("Subject").Value
}).ToList();
Linq XML query the descendants of parent with a specific value in attribute
You need to use root.Descendants
instead of root.Elements
.
For example,
IEnumerable<XElement> xElements = from element in root.Descendants("TestDriveRequest")
where element.Attribute("Record").Value == "1"
select element;
XContainer.Elements
Returns a filtered collection of the child elements of this element or
document, in document order. Only elements that have a matching XName
are included in the collection.
XContainer.Descendants
Returns a filtered collection of the descendant elements for this
document or element, in document order. Only elements that have a
matching XName are included in the collection Output
Complete Code
IEnumerable<XElement> xElements = from element in root.Descendants("TestDriveRequest")
where element.Attribute("Record").Value == "1"
select element;
foreach (XElement el in xElements.Descendants().Where(p => !p.HasElements))
{
int keyInt = 0;
string keyName = el.Attribute("name").Value;
while (keyValuePairs.ContainsKey(keyName))
{
keyName = $"{el.Attribute("name").Value}_{keyInt++}";
}
keyValuePairs.Add(keyName, el.Value);
}
Sample Output
Understanding Linq To Xml - Descendants return no results
var result = doc.Descendants("TransactionInformationType");
selects all descendants in the XDocument that have element name "TransactionInformationType"
and are in the empty namespace.
From you screenshot it seems the element you're trying to select is in the namespace "https://ssl.ditonlinebetalingssystem.dk/remote/payment"
though.
You need to specify that explicitly:
XNamespace ns = "https://ssl.ditonlinebetalingssystem.dk/remote/payment";
↑↑ ↑
var result = doc.Descendants(ns + "TransactionInformationType");
Using LINQ to XML to get specific descendants node value
The LINQ-to-XML methods Elements()
and Descendants()
only deal with single names, not xpath-like paths. If you want to give an xpath expression, use the xpath extensions.
// using System.Xml.Linq;
var status = (string)xml.XPathSelectElement("//result/status");
Otherwise you need to build up an equivalent query using the methods correctly.
var status = (string)xml.Descendants("result").Elements("status").FirstOrDefault();
LINQ to XML - Unable to read Elements or Descendants from XDocument or XElement
You already have access to those elememts. There is just a missonception on what Console.WriteLine
does .
Console.WriteLine
just call ToString
over the thing.
For XNode
the ToString
is overriden to return a Xml string. source
It will work for xEl.Element
as it's return an XElement
.XElement
is a XContainer
, that is an XNode
. So XNode overriden method will be called.
But not for Descendants as it return a collection waiting to be enumerated. Lazy linQ.
In order to display your result you can either enumerate the decendants and call ToString
on them.
foreach(var el in xEl.Descendants("Student")){
Console.WriteLine(el);
}
Here is an Example: Live Demo
var input = @"<?xml version=""1.0"" encoding=""utf-8""?>
<Students>
<Student>
<Ordinal>1</Ordinal>
<Name>Student1</Name>
<BirthDate>Date1</BirthDate>
<ID>ID1</ID>
</Student>
<Student>
<Ordinal>2</Ordinal>
<Name>Student2</Name>
<BirthDate>Date2</BirthDate>
<ID>ID2</ID>
</Student>
</Students>";
// Parse string is equivalent to load path.
XDocument xDoc = XDocument.Parse(input);
XElement xEl = XElement.Parse(input);
var xDocString = xDoc.ToString();
var XElElement = xEl.Element("Student").ToString();
// => <Student> <Ordinal>1</Ordinal> ...
var XElDecendant = xEl.Descendants("Student").ToString();
// => System.Xml.Linq.XContainer+<GetDescendants>d__39
// I'm a linQ container containing XNode, please enumerate me.
var xDocElement = xDoc.Element("Student").ToString();
//=> null
var xDocDecendant = xDoc.Descendants("Student").ToString();
// => System.Xml.Linq.XContainer+<GetDescendants>d__39
// I'm a linQ container containing XNode, please enumerate me.
foreach(var el in xEl.Descendants("Student")){
Console.WriteLine(el);
}
C# - LINQ to XML - Find Descendants where the attribute starts with something
Your issue is that x.Parent.Parent.Attribute("Name")
doesn't exist for the first Shape
in your Xml. The parent of the parent is PageContents
root of the document and that doesn't have a Name
attribute.
You could solve easily like this:
var datasets =
pageContents
.Descendants(nameSpace + "Shape")
.Where(x => x.Parent.Parent.Name == nameSpace + "Shape")
.Where(x => x.Parent.Parent.Attribute("Name").Value.StartsWith("CFF Container"));
You can also write your whole code like this:
public string XmlFindSurveyName(XElement pageContents, XNamespace nameSpace) =>
(
from shape in pageContents.Descendants(nameSpace + "Shape")
where shape.Parent.Parent.Name == nameSpace + "Shape"
where shape.Parent.Parent.Attribute("Name").Value.StartsWith("CFF Container")
where shape.Element(nameSpace + "Text") != null
select shape.Element(nameSpace + "Text").Value
).FirstOrDefault() ?? "";
Related Topics
Lock (Monitor) Internal Implementation in .Net
Parse Datetime in Multiple Formats
Notify Binding for Static Properties in Static Classes
How to Run and Interact with an Async Task from a Wpf Gui
Random Number Between 2 Double Numbers
Http 404 Page Not Found in Web API Hosted in Iis 7.5
Dependency Injection Using Azure Webjobs Sdk
ASP.NET Identity - Multiple Object Sets Per Type Are Not Supported
How to Pass User Defined Table Type as Stored Procedured Parameter in C#
Call a Method from Another Form
No Templates in Visual Studio 2017
C#: Prepending to Beginning of a File
How to Set Up HTML/Email Templates with ASP.NET
Optimal Way to Read an Excel File (.Xls/.Xlsx)
Export Datatable to Excel with Open Xml Sdk in C#
The Entity Type Applicationuser Is Not Part of the Model for the Current Context