I have the following XML:
<PlayerSetup>
<CardDeck name="deckOfCards"/>
<Card name="one"/>
<Card name="two"/>
<Card name="three"/>
<Card name="four"/>
<Token name="four"/>
</PlayerSetup>
I need to retrieve only the elements which attributes name="four", I have the following code:
var query = from d in xdoc.Descendants("PlayerSetup")
where (string)d.Attribute("name").Value == "four"
select d;
Which of course, is not working, returns no elements. Any idea ? Thanks.
If you want the descendant elements under the "PlayerSetup" elements with name="four", you can do:
var query = from d in xdoc.Descendants("PlayerSetup").Descendants()
where (string)d.Attribute("name") == "four"
select d;
If you want the "PlayerSetup" elements who have at least one descendant element with name="four", you can do:
var query = from d in xdoc.Descendants("PlayerSetup")
where d.Descendants().Any(c => (string)c.Attribute("name") == "four")
select d;
You are wanting to look at the Descendants of PlayerSetup, so grab those:
var query = from d in xdoc.Descendants("PlayerSetup").Descendants()
where d.Attribute("name")?.Value == "four"
select d;
//query.Count == 2
this solution uses C#6 syntax
I think your problem is already known for a long time.
You can find it here:
LINQ to XML query attributes
or here:
How to get attribute names of element in xml by LINQ in C#
:)
Related
I have a problem here which i can solve using Xpath with c#. However I am trying to do it via LINQ and i am just not able to figure out how to do it.
I have a repeating Group2 which has several child nodes. I want to select "Group2" based on the value of an element in group 2 descendants.
thx for the help in advance. here is a sample xml.
<Receive>
<Idoc>
<EDI_DC40>
<TabName>EDI_DC40</TabName>
</EDI_DC40>
<Group1>
<Group2>
<Group3>
<SelectGroup2BasedonValueofThisNode>001</SelectGroup2BasedonValueofThisNode>
</Group3>
</Group2>
<Group2>
<Group3>
<SelectGroup2BasedonValueofThisNode>002</SelectGroup2BasedonValueofThisNode>
</Group3>
</Group2>
<Group2>
<Group3>
<SelectGroup2BasedonValueofThisNode>003</SelectGroup2BasedonValueofThisNode>
</Group3>
</Group2>
</Group1>
</Idoc>
var _selectedGroup = document.Descendants("Group2");
This selects all the Group2 -- but what next i am not sure how to put the where or select.
var element = _cloneDocument.Descendants("Group2").Where(x => x.Value == "001");
well i just tried above code and it seems to work. My next question -- is this the right way to do it? Or there is another more recommended way to do this.
You need to use LINQ2XML inside the where delegate, like this:
var element = doc
.Descendants("Group2")
.Where
(
x => x.Descendants("SelectGroup2BasedonValueofThisNode").Single().Value == "001"
)
.Single();
This will give you:
<Group2>
<Group3>
<SelectGroup2BasedonValueofThisNode>001</SelectGroup2BasedonValueofThisNode>
</Group3>
</Group2>
Click here for the example on DotNetFiddle.
i want to find the value of an Attribute in our XML-FIle.
For Example, here our XML Document:
<PROJECT_DOCUMENTS>
<DOCUMENT isFile="YES" isLink="YES" type="Risk Action List (combined)" path="path" showFile="" showFolder="YES" FilePath="" FolderPath="" />
<DOCUMENT isFile="YES" isLink="NO" type="ASPICE-Action List" path="path" showFile="" showFolder="YES" FilePath="" FolderPath="path" />
</PROJECT_DOCUMENTS>
I want the value of path = " .... bla ..." were the type is ASPICE-Action List.
Here my code with i generated:
XElement elementToChange = (from c in getFileFromXML.Element("PROJECT_DOCUMENTS")
.Elements("DOCUMENT")
where("type"== "ASPICE-Action List")
select c).Single().Element("path");
But i dont get the infomation. I think the type == Aspice-Action list doesn´t work.
Can anyone help me to solve my Problem ;)
Thanks
If you want to get path attribute values you can use next code which manipulate with Attribute() method for attributes of element:
var elementsToChange = from c in getFileFromXML.Element("PROJECT_DOCUMENTS").Elements("DOCUMENT")
where c.Attribute("type")?.Value == "ASPICE-Action List" )
select c.Attribute("path").Value;
To retrieve whole elements use next code or add SingleOrDefault() or FirstOrDefault() to the end for your tasks:
var elementsToChange = from c in getFileFromXML.Element("PROJECT_DOCUMENTS").Elements("DOCUMENT")
where c.Attribute("type")?.Value == "ASPICE-Action List" )
select c;
You can try getting the same using the code below:
from c in getFileFromXML.Elements("DOCUMENT")
where "ASPICE-Action List" == c.Attribute("type").Value
select c;
Still a Linq newbie here, and now having issues with the WHERE clause. I'm trying to return anything found in the printer tags, but only from below the element list type="lff".
If I try to output the descendant elements with no WHERE clause, I get everything (from both <list> elements). When I try to add various versions of a WHERE clause, I get nothing back. I'm obviously not putting the WHERE condition in correctly.
(I need to get the element object, so I can check the NAME and the VALUE. In my example below, I am only outputting the VALUE for now).
Can you advise?
Here is the XML:
<?xml version="1.0"?>
<printerlist>
<list type="aff">
<printserver>print-server1</printserver>
<printserver>print-server2</printserver>
<printserver>print-server3</printserver>
<additionalprinters>
<printer>
<fullname>\\servera\bbb</fullname>
</printer>
</additionalprinters>
</list>
<list type="lff">
<printserver>print-sever4</printserver>
<additionalprinters>
<printer>
<fullname>\\serverb\bbb</fullname>
</printer>
<printer>
<fullname>\\serverc\aaa</fullname>
</printer>
</additionalprinters>
</list>
</printerlist>
And here is the code to try and get the list:
var qq = from c in xml.Descendants("additionalprinters").Descendants("printer")
//where (string) c.Parent.Attribute("type") == "lff"
//Uncommenting the above line means that nothing is returned.
select c;
foreach (XElement q in qq)
{
Console.WriteLine("Test Output: {0}", q.Value );
}
Output is:
Test Output: \\servera\bbb
Test Output: \\serverb\bbb
Test Output: \\serverc\aaa
I am only looking for the final two outputs to be returned, in this particular case.
The parent of printer is additionalprinters and it doesn't have type property, you need to use .Parent twice to get list element.
from c in xml.Descendants("additionalprinters").Descendants("printer")
where (string) c.Parent.Parent.Attribute("type") == "lff"
select c
Or you can also do the following
xml.Descendants("list")
.Where(c => (string) c.Attribute("type") == "lff")
.SelectMany(x => x.Element("additionalprinters").Descendants("printer"))
You can also use XPath selector from System.Xml.XPath namespace for this purpose:
var doc = XDocument.Parse(xml);
var printers = doc.XPathSelectElements("//list[#type='lff']/additionalprinters/printer");
I have an XML document that contains a series of item nodes that look like this:
<data>
<item>
<label>XYZ</label>
<description>lorem ipsum</description>
<parameter type="id">123</parameter>
<parameter type="name">Adam Savage</parameter>
<parameter type="zip">90210</parameter>
</item>
</data>
and I want to LINQ it into an anonymous type like this:
var mydata =
(from root in document.Root.Elements("item")
select new {
label = (string)root.Element("label"),
description = (string)root.Element("description"),
id = ...,
name = ...,
zip = ...
});
What's the best way to pull each parameter type according to the value of its 'type' attribute? Since there are many parameter elements you wind up with root.Elements("parameter") which is a collection. The best way I can think to do it is like this by method below but I feel like there must be a better way?
(from c in root.Descendants("parameter") where (string)c.Attribute("type") == "id"
select c.Value).SingleOrDefault()
I would use the built-in query methods in LINQ to XML instead of XPath. Your query looks fine to me, except that:
If there are multiple items, you'd need to find the descendants of that instead; or just use Element if you're looking for direct descendants of the item
You may want to pull all the values at once and convert them into a dictionary
If you're using different data types for the contents, you might want to cast the element instead of using .Value
You may want to create a method to return the matching XElement for a given type, instead of having several queries.
Personally I don't think I'd even use a query expression for this. For example:
static XElement FindParameter(XElement element, string type)
{
return element.Elements("parameter")
.SingleOrDefault(p => (string) p.Attribute("type") == type);
}
Then:
var mydata = from item in document.Root.Elements("item")
select new {
Label = (string) item.Element("label"),
Description = (string) item.Element("description"),
Id = (int) FindParameter(item, "id"),
Name = (string) FindParameter(item, "name"),
Zip = (string) FindParameter(item, "zip")
};
I suspect you'll find that's neater than any alternative using XPath, assuming I've understood what you're trying to do.
use XPATH - it is very fast ( except xmlreader - but a lot of if's)
using (var stream = new StringReader(xml))
{
XDocument xmlFile = XDocument.Load(stream);
var query = (IEnumerable)xmlFile.XPathEvaluate("/data/item/parameter[#type='id']");
foreach (var x in query.Cast<XElement>())
{
Console.WriteLine( x.Value );
}
}
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