LINQ: Remove Element from XML based on attribute value? - c#

How can I remove any element in xml based on matched attribute value?
Here is my XML :
<Projects>
<Project serverUrl="tcp://xyz1:xxx/sdfsdf.rem" projectName="project1" />
<Project serverUrl="tcp://xyz2:xxx/sdfsdf.rem" projectName="project2" />
<Project serverUrl="tcp://xyz3:xxx/sdfsdf.rem" projectName="project3" />
<Project serverUrl="tcp://xyz4:xxx/sdfsdf.rem" projectName="project4" />
<Project serverUrl="tcp://xyz5:xxx/sdfsdf.rem" projectName="project5" />
<Project serverUrl="tcp://xyz6:xxx/sdfsdf.rem" projectName="project6" />
</Projects>
I am using the following LINQ query:
var remove = from elemet in xdoc.Elements("Projects").Elements("Project")
where elemet.Attribute("projectName").Value == "project1"
select elemet.Parent.Remove();
I am getting compile time error on select as :
The type of expression in select
clause is Incorrect
EDIT ANSWER:
this one works for me. Thanks All
var xElement = (from elemet in xdoc.Elements("Projects").Elements("Project")
where elemet.Attribute("projectName").Value == foundProject
select elemet);
xElement.Remove();

Remove is a (void) method call, not a function that can return a value. You probably want something like this:
var elementsToRemove = from elemet in xdoc.Elements("Projects").Elements("Project")
where elemet.Attribute("projectName").Value == "project1"
select elemet;
foreach (var e in elementsToRemove)
e.Remove();
LINQ is a query language, it's (mainly) used to return something. Performing actions on these elements is usually a separate step.

You could use
xdoc.Elements("Projects").Elements("Project").Where(
elemet => elemet.Attribute("projectName").Value == "project1")
.ToList().ForEach(i => i.Remove());
or
(from elemet in xdoc.Elements("Projects").Elements("Project")
where elemet.Attribute("projectName").Value == "project1"
select elemet).ToList().ForEach(i => i.Remove());

You can use the following code snippet:
xdoc.XPathSelectElement("Projects/Project[#projectName = 'project1']").Remove();

Remove() is a method you call on an XNode. Your query is trying to select the method which doesn't make any sense.
What you actually want to do is select the item you wish to remove, then call the Remove() method on the selected item. You can find an example here:
XNode.Remove Method

Related

LINQ - simple select based on value of child element

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.

Find one attribute in XML File with linq.XML

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;

C# LINQ XML filtering by attribute

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#
:)

Get an Array by selection of a block from xml-File

I have to write a program using Linq. I'm just a student and I didn't learned that yet, so I have two questions:
What would be a good book/ebook... to teach myself what my next question will be about?
I have an XML-File, that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Projects>
<Project>
<Information>
<Name>Project1</Name>
<Date>26.01.2015</Date>
</Information>
<Files ID = "S" path = "C:\Users\marcel\Documents">
<file>Test1.txt</file>
<file>Test2.txt</file>
<file>Test3.txt</file>
<file>Test4.txt</file>
<file>Test5.txt</file>
</Files>
<Files ID = "C" path = "C:\Users\marcel\Documents">
<file>Test1(1).txt</file>
<file>Test1(2).txt</file>
<file>Test1(3).txt</file>
<file>Test1(4).txt</file>
<file>Test1(5).txt</file>
</Files>
</Project>
I want to get a string-array which is containing the values of the "file" elements, depenging on ID=S or C.
I have more than 1 project in there, so it first has to be searched by name, that's working right now:
var entries = from items in xelement.Elements("Project")
where (string)items.Element("Name").Value == projectName
select items;
that gets me the whole block of the needed project.
Can I use the result of the first command for getting the filenames?
Or can I just extend the code of the first part?
To get a specific Project element having the specified name you can use First:
var projectElement = xElement
.Elements("Project")
.First(x => (String) x.Element("Information").Element("Name").Value == projectName);
In a similar way you can find the desired Files element by specifying a value for the ID attribute:
var filesElement = projectElement
.Elements("Files")
.First(x => x.Attribute("ID").Value == id);
You can then use Select to project the File elements to their values and convert that to an array:
var files = filesElement
.Elements("file")
.Select(x => (String) x.Value)
.ToArray();
Note that this code will throw exceptions if the XML has an unexpected format. E.g., if First does not find a matching element an exception is thrown. Also, the Element method will return null if the specified element is not found and thus code like x.Element("Information").Element("Name") will throw an exception if there is no Information element because the next call to Element is performed on the null reference.
Thank you Martin, that worked :)
I just came up with an own solution looking like this:
var files = from file in entries.Elements("Files").Elements("file")
where (string)file.Parent.Attribute("ID").Value == cOrS
select file.Value;

Filter descendants parent [or at least higher level than results]

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");

Categories

Resources