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.
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 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;
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#
:)
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 was a long time user of XmlTextReader but after noting that the answer to every related question on here was 'use LINQ' I decided to take the plunge, everything has been going well up until this point. I've been banging my head against this for a while, hopefully someone can help.
I have the following section in my document;
<action_areas>
<time_slice name="0 - 5" id="1">
<action_area id="1">
<aa_team id="1000">9</aa_team>
<aa_team id="1001">7</aa_team>
</action_area>
<action_area id="2">
<aa_team id="1000">5</aa_team>
<aa_team id="1001">2</aa_team>
Due to the structure of the document I need "action_areas" to be the thing passed to the initial selection, ie;
var stuff = from item in xDoc.Descendants("action_areas")
So the question is what statements do I follow that with so I can filter based on the time_slice name attribute AND the action_area id attribute AND the aa_team id attribute so I can ultimately get at the content contained in the aa_team element (9, 7, 5 and 2 in the above instance) with the 'select new' statement?
Thanks in advance.
Try this
string timeSliceToSearchFor = "0 - 5";
string actionAreaToSearchFor = "1";
string aaTeamIdToSearchFor = "1001";
// Returns 7
string value = xDoc .Descendants("action_areas")
.Elements("time_slice")
.Where(element => element.Attribute("name").Value == timeSliceToSearchFor)
.Elements("action_area")
.Where(element => element.Attribute("id").Value == actionAreaToSearchFor)
.Elements("aa_team")
.Where(element => element.Attribute("id").Value == aaTeamIdToSearchFor)
.Single().Value;