LINQ to XML - Where clause - c#

I have an XML file with 2 types of information - Locations and job types which is determined by the SLvl value. I wish to bind the SearchTxt value for these to 2 drop downs (one for locations, one for job types) to be used as filters on my page.
The problem is I cant quite get my where clause to filter on the SLvl value. With the where clause in no results are returned. If I remove it the query does return all the text values.
C#
using System.Xml.Linq;
using System.Linq;
.....
// Loading from file
XDocument loaded = XDocument.Load(#"http://[LINKREMOVED]/vacancies.aspx");
// Query the data
var q = (from c in loaded.Descendants("items")
where c.Element("SLvl").ToString() == "0"
select c.Element("SearchTxt").ToString()).Distinct();
// Populate drop down
foreach(string name in q)
{
ddlLocation.Items.Add(new ListItem(name, name));
}
XML:
<VacancyMatch>
<items>
<SearchID>60</SearchID>
<SearchTxt>Scotland</SearchTxt>
<ParentID>0</ParentID>
<SearchCatID>1</SearchCatID>
<SLvl>1</SLvl>
<SubCat>1</SubCat>
</items>
<items>
<SearchID>92</SearchID>
<SearchTxt>Accounting</SearchTxt>
<ParentID>60</ParentID>
<SearchCatID>2</SearchCatID>
<SLvl>2</SLvl>
<SubCat>2</SubCat>
</items>
... More items here
</VacancyMatch>
I guess the problem is that the data is at the same level? Its my first time using LINQ to XML so any help is greatly appriciated.
Note:
XML is provided by a third party so formatting is up to them.

Drop the .ToString() and use .Value property instead:
var values = loaded.Descendants("items")
.Where(i => i.Element("SLvl").Value == "0")
.Select(i => i.Element("SearchTxt").Value)
.Distinct();
Calling ToString() on XElement will return entire node as text. For example, if we change i.Element("SearchTxt").Value in the query above to i.Element("SearchTxt").ToString() it will produce strings such as:
<SearchTxt>Accounting</SearchTxt>
Accessing Value property will extract node's inner text - "Accounting" in this case.

This:
where c.Element("SLvl").ToString() == "0"
should be:
where c.Element("SLvl").Value == "0"
You can't get a value of an element with "ToString()" method, you need to read it's "Value" property instead.
Same goes for any other lines where you are trying to get a value of an element.
Hope it helps.

I notice one problem, you use ToString() on the XElements which is not exactly what you want, I think :), to get the text content of a XElement use the Value property.
http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.value.aspx

Related

Where clause in Linq in List c#

I have a struct like this:
struct Test
{
string name;
string family;
public Test...
}
in my code I have a list of this struct:
List<Test> testList=new List<Test>();
I have a linq expression like this:
var list =testList.Select(n=>n.Name);
but how can I filter this selection by Family of the testList? something like this:
var list=testList.Select(n=>n.Name).Where(f=>f.Family=="");
this Where clause just apply on selected Names which are a list of string
Any ideas how to do that?
Just put the Where before the Select:
var list=testList.Where(f=>f.Family=="").Select(n=>n.Name);
In Linq you need to apply the filter before projecting (unless the filter applies to the results of the projection rather than the original collection).
Filter using Where before you select just one property using Select. That way, you still get the full object:
testList.Where(t => t.Family == "").Select(t => t.Name)
After all, Select will take the object and then only pass on whatever you return in the lambda. In this case, you only return a string, so you throw all the other information from the Test object away. And as such, the information you want to filter on isn’t available anymore.
If you switch that around, you can filter on the test object, and then return only that one string.
Normally when I am checking for an empty value like that, I'll use string.IsNullOrEmpty(), just in case.
testList.Where(f=> string.IsNullOrEmpty(f.Family)).Select(n=>n.Name);
You should first apply where clause and the select your desired data:
var list=testList.Where(f=>f.Family=="").Select(n=>n.Name);

Searching String to Xml file in C#

<AccessUrl>
<Url>/User/Index</Url>
<Url>/EmailAccounts/Index</Url>
<Url>/Registration/Index</Url>
</AccessUrl>
I have a xml file. I wand to search a string in it. I have written a C# code for it.
string Url="/User/Index";// or One of the above value from file.
var CheckPermission = (from a in doc.Elements("AccessUrl")
where a.Element("Url").Value.Contains(Url)
select a).Any();
but it only works OK with first Url value from file. But if i change the string Url="/EmailAccounts/Index";. It doesn't show any match even the url value exists in xml file.Well i tried a lot but didn't get succeed yet.
This is because a.Element("Url") gives you the first child element with specified name. You should go through all children instead with something like:
a.Elements("Url").Any(u => u.Value.Contains(Url))
This is because you are calling:
where a.Element("Url")
This will return the first matching element.
From the docs:
Gets the first (in document order) child element with the specified System.Xml.Linq.XName
You need to use something like this to check all elements:
var CheckPermission = (from a in doc.Descendants("Url")
where a.Value.Contains(Url)
select a).Any();
This appears to work.
string url="/EmailAccounts/Index";// or One of the above value from file.
var checkPermission = xml.Elements("AccessUrl").Elements("Url").Any(x => x.Value == url);

Using C # LINQ to Select within 2 tags that has a descendant node equal to a certain value?

I'm trying to select all objects within 2 tags specifically <AR>'s that contains an element that's a descendant of <AR>: <RL> with a certain value say 2. <RL> can be buried an arbitrary number of levels within <AR>'s, but will always be within <AR>. How can I do this in LINQ?
EX1:
<ARS>
<AR>
<EI> </EI>
<RL>5</RL>
</AR>
<AR>
<EI> </EI>
<RL>2</RL>
</AR>
</ARS>
Result :
<AR>
<EI> </EI>
<RL>2</RL>
</AR>
I tried using
IEnumerable<XNode> test_var = from result in doc.Descendants("AR")
where result.DescendantNodes()
But go from here, but this threw an error msg
var test_var = from result in doc.Descendants("AR")
where result.Descendants("RL").Any(x => (int)x == 2)
select result;
First problem is you need a select statement, such as adding select result.
Additionally your where needs to be a boolean. It sounds like you are looking for existence, which is frequently handled via the Any() extension method.
var searchString = "2";
IEnumerable<XElement> test_var = from result in doc.Descendants("AR")
where result.Descendants("RL").Any(xelm => xelm.Value == searchString)
select result;

Retrieve SiteMapNode matching custom attribute using LINQ

I am new to LINQ. I have an ordinary siteMap XML document with custom attributes. One of these attributes is: id
I would like to use LINQ to retrieve a single node matching the value of the custom attribute (id).
etc.
My attempt at the LINQ looks like this:
private SiteMapNode FindNodeById(SiteMapNodeCollection nodes, int siteMapNodeId)
{
var pageNode = from SiteMapNode node in nodes.Cast<SiteMapNode>()
where node["id"] == Convert.ToString(siteMapNodeId)
select node;
return (SiteMapNode)pageNode;
}
During debugging, pageNode becomes assigned with:
{System.Linq.Enumerable.WhereEnumerableIterator<System.Web.SiteMapNode>}
And on the return statement an InvalidCastException is thrown:
Unable to cast object of type 'WhereEnumerableIterator`1[System.Web.SiteMapNode]' to type 'System.Web.SiteMapNode'.
Any help is appreciated! :)
EDIT: I've re-posted this question in a clearer manner here: Re-worded Question
Thanks to Stefan for putting me on the right track!
You try to cast a IEnumerable<SiteMapNode> to a SiteMapNode. Use First to filter and return one node:
return nodes
.Cast<SiteMapNode>()
.First(node => node["id"] == Convert.ToString(siteMapNodeId));
pageNode is a sequence of nodes.
You want to call First() to get the first item in the sequence.

Linq to XML Remove multiple returns

Hi have a Linq Query to extract some information. Following is the part of it.
node = "DocumentClass";
AVariable="Something"
na="NA";
var documentClassesScript = (from documentClass in configparentXML.Descendants(node)
where documentClass.Attribute("Name").Value.Contains(AVariable)
select new ReadingXmlWithLinq
{
CustomStorageString = documentClass.Element("ValidationPluginAssociations") != null ? documentClass.Descendants("ValidationPluginAssociation").Attributes("CustomStorageString").Single().Value : na,
}
).Distinct();
In Some case i got following error
Sequence contains more than one
element
The reason is ValidationPluginAssociations contain more than one ValidationPluginAssociation. I need to distinct and get only one of them. Is there any way to get it.
if there is no need to have single object you can use First:
documentClass.Descendants("ValidationPluginAssociation")
.Attributes("CustomStorageString").First().Value

Categories

Resources