C# Linq to XML query - c#

<World>
<Animals>
<Tab>
<Dogs id ="1">
<Dog1></Dog1>
<Dog2></Dog2>
<Dog3></Dog3>
</Dogs>
<Dogs id ="2"></Dogs>
<Dogs id ="3"></Dogs>
</Tab>
</Animals>
</World>
How do I get all elements under tag where id == 1?
My Linq query. (doesn't work) why?
XDocument xml= XDocument.Load(xml.xml);
var elements = from e in xml.Descendants("Animals").Descendants("Tab").Elements("Dogs")
where e.Attribute("id").toString().Equals("1")
select c;
Could you check it please?
Thanks!

var result = xdoc.Descendants("World")
.Descendants("Animals")
.Descendants("Tab")
.Elements("Dogs")
.Where(n => n.Attribute("id").Value == "1");
Output:
<Dogs id="1">
<Dog1></Dog1>
<Dog2></Dog2>
<Dog3></Dog3>
</Dogs>

Or use XPath:
xml.XPathSelectElements("/World/Animals/Tab/Dogs[#id=1]")
or
xml.XPathSelectElements("//Dogs[#id=1]")
which would find all Dogs wherever they occurred.

From your sample data I think you want
//from e in xml.Descendants("Animals").Descendants("Tab").Elements("Dogs")
from e in xml.Descendants("Animals").Elements("Tab").Descendants("Dogs")
And in the same line, Descendants("Animals") could be Elements("Animals") if you want to enforce the structure.
For the rest your query looks OK.

Related

use LINQ on XmlNodeList

<X version="1.0">
<Y id="abc" abv="a"/>
<Y id="edf" abv="e"/>
</X>
I want to select the node whose id is "abc", and return its abv "a".
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlNodeList list = doc.SelectNodes("X/Y");
var node = list.Cast<XmlNode>().Where(node => node["id"].InnerText == "abc")
.Select(x=>x["abv"].InnerText);
But it does't work, node["id"].InnerText is always "". Can you point out where is a problem?
Thanks a lot
Aside from the fact what your code snippet wouldn't be compiled because of non-unique node variable (first outside of linq query and second in "where" method lambda), you have also missed Attributes in your query.
It should be something like
var node = list.Cast<XmlNode>()
.Where(n => n.Attributes["id"].InnerText == "abc")
.Select(x => x.Attributes["abv"].InnerText);
The InnerText for a node is the text that appears between <node> and </node>. So for, eg <Y attributes /> there is no inner text.
You need to use node => node.Attributes["id"].Value == "abc"
Just cast XmlNodeList to List, like that:
List<XmlNode> list = new List<XmlNode>();
foreach(XmlNode a in xmlNodeList)
{
list.Add(a);
}
list.OrderBy((element) => element.ChildNodes[0].InnerText);

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

LINQ Selecting particular element

Basically, I am having trouble trying to understand where I am going wrong in here..
Basically, I have the following XML:
<Directory>
<CustDirectory name="Name1">
<Person name="Foo" />
<Person name="Goo" />
<Person name="Gentu" />
</CustDirectory>
<CustDirectory name="Name2">
<Person name="F22" />
<Person name="Gentu" />
</CustDirectory>
</Directory>
Using forms, I am updating a list of contacts and I want to write the list to XML depending on which category (stored as a string).
What I have decided to use is therefore LINQ to do it, but, I can't seem to figure out there .Where and have read through questions on stackoverflow and can't seem to figure it out.
Here is my attempt:
var con = e.Element("Directory").Element("CustDirectory").Descendants("Person").Where(p => p.Attribute("name").Value.ToStr == "Name2");
This does not work and throws up a null exception... When I take off the .Where clause, the data contained in the descendent shows correctly.
Could anyone please tell me where I am going wrong in terms of the LINQ query so I can select all the descendants of a particular root?
If I understood correctly your question you are trying to extract all the Person elements that belongs to a given CustDirectory. In this case you should use something more like:
var con = e.Element("Directory").Descendants("CustDirectory").Where(p => p.Attribute("name").Value == "Name2").Elements("Person");
Everything looks right except for the ToStr part.
To select only the Person elements under the CustDirectory with the name Name2 you will need to put your Where on that, like this:
var con = e.Element("Directory").Elements("CustDirectory")
.First(cd => cd.Attribute("name").Value == "Name2").Descendants("Person");
Note that I changed Element("CustDirectory") to Elements("CustDirectory").
You don't need Element("Directory") at the beginning, because e it self is already referenced <Directory> (different case if e is an XDocument instead of XElement as you said in comment). This example able to return <Person> nodes give sample XML posted in question :
var e = XElement.Parse("...");
var con = e.Elements("CustDirectory")
.Where(p => p.Attribute("name").Value == "Name2")
.Elements("Person");
void Main()
{
var xml = #"<Directory>
<CustDirectory name=""Name1"">
<Person name=""Foo""/>
<Person name=""Goo""/>
<Person name=""Gentu""/>
</CustDirectory>
<CustDirectory name=""Name2"">
<Person name=""F22""/>
<Person name=""Gentu""/>
</CustDirectory>
</Directory>";
var xmlDoc = XDocument.Parse(xml);
var con = xmlDoc.Element("Directory")
.Elements("CustDirectory")
.Where(p => p.Attribute("name").Value == "Name2")
.Descendants("Person")
// added bonus to get a specific node
.Where(p => p.Attribute("name").Value == "F22");
Console.WriteLine(con);
}

remove sections of XML document with Linq

How would i using Linq remove all section where their element contains parameter with {} ? In my example i want to remove section with {SecName1}
Source document:
<ReceiptLayoutMaintenanceRequest>
<ReceiptLayoutName>Test Layout1</ReceiptLayoutName>
<ActionName>Add</ActionName>
<ReceiptLayoutForMaintenance>
<Name>Test Layout1</Name>
<Description>ReciptDesc</Description>
<PrinterName>Emulator - Receipt</PrinterName>
<ReceiptLayout>
<Name>AAA</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
<ReceiptLayout>
<Name>{SecName1}</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
</ReceiptLayoutForMaintenance>
</ReceiptLayoutMaintenanceRequest>
Wanted output
<ReceiptLayoutMaintenanceRequest>
<ReceiptLayoutName>Test Layout1</ReceiptLayoutName>
<ActionName>Add</ActionName>
<ReceiptLayoutForMaintenance>
<Name>AAA</Name>
<Description>ReciptDesc</Description>
<PrinterName>Emulator - Receipt</PrinterName>
<ReceiptLayout>
<Name>AAA</Name>
<Description>$</Description>
<TemplateName>DefaultTemplate</TemplateName>
<LayoutParameters />
</ReceiptLayout>
</ReceiptLayoutForMaintenance>
thanks
This removes any ReceiptLayout node which has a child Name that starts and ends with brackets and produces your desired output:
XDocument doc = XDocument.Load(#"test.xml"); //load xml
var nodesToRemove = doc.Descendants("ReceiptLayout")
.Where(x => x.Element("Name").Value.StartsWith("{")
&& x.Element("Name").Value.EndsWith("}"))
.ToList();
foreach (var node in nodesToRemove)
node.Remove();
This can be shortened into one Linq statement, personally I prefer to keep Linq query and modification (removal) separate though:
doc.Descendants("ReceiptLayout")
.Where(x => x.Element("Name").Value.StartsWith("{")
&& x.Element("Name").Value.EndsWith("}"))
.Remove();
var doc = XDocument.Parse(xml);
doc.Descendants()
.Where(n => !n.HasElements && Regex.IsMatch(n.Value, "[{].*?[}]"))
.Select(n=>n.Parent) // because you want to remove the section not the node
.Remove();
xml = doc.ToString();
A variant of BrokenGlass code using the let keyword
var doc = XDocument.Load(#"test.xml");
var list = from p in doc.Descendants("ReceiptLayout")
let q = p.Element("Name")
let r = q != null ? q.Value : string.Empty
where r.StartsWith("{") && r.EndsWith("}")
select p;
list.Remove();
This is "premature optimization" :-) I "cache" the p.Element("Name").Value. Ah... And I check if really there is a Name Element so that everything doesn't crash if there isn't one :-)

Linq to XML Question

Given the following XML, what query can I use to extract the value of preapprovalKey to a string variable? Still a little new to LINQ to XML.
<?xml version="1.0" encoding="UTF-8" ?>
- <ns2:PreapprovalResponse xmlns:ns2="http://svcs.paypal.com/types/ap">
- <responseEnvelope>
<timestamp>2011-04-05T18:35:32.952-07:00</timestamp>
<ack>Success</ack>
<correlationId>7cec030fa3eb2</correlationId>
<build>1655692</build>
</responseEnvelope>
<preapprovalKey>PA-9AG427954Y7578617</preapprovalKey>
</ns2:PreapprovalResponse>
XDocument doc = XDocument.Load("test.xml");
string preapprovalKey = doc.Descendants("preapprovalKey").Single().Value;
See below my exmaple, it help you to resolve your issue and problem. :)
Consider this below XML is there as one of the SQL table's column.
<Root>
<Name>Dinesh</Name>
<Id>2</Id>
</Root>
The objective of the query is to fetch the Name from the XML. In this example we will fetch the 'Dinesh' as the value.
var Query = (from t in dbContext.Employee.AsEnumerable()
where t.active == true
select new Employee
{
Id = t.AtpEventId,
Name = XDocument.Parse(t.Content).Descendants("Root").Descendants("Name").ToList().
Select(node => node.Value.ToString()).FirstOrDefault()
});
Note the following :-
Here in above LINQ , t.active == true is just an example to make some condition if needed.
Please note, in the above LInQ query, always use the AsEnumerable(), as I did in the
first file of the Linq query.exmaple(var Query = (from t in dbContext.Employee.AsEnumerable())
Descendants("Root").Descendants("Name") , Here Root should be the Element matching with the XML, And under the Root we have Name element, thats why we wrote
Descendants("Root").Descendants("Name")
For any further clarification you can reach me via danish.eggericx#gmail.com

Categories

Resources