Conditional Output by Linq to XML - c#

Below is the sample XML that I am trying to read using Linq to XML:
<root>
<Employee>
<Name>Jeff</Name>
<Department>Account</Department>
</Employee>
<Employee>
<Name>David</Name>
<Department>Finance</Department>
</Employee>
<Employee>
<Name>Neil</Name>
<Department>Sales</Department>
</Employee>
<Employee>
<Name>Jason</Name>
<Department>Retail</Department>
</Employee>
</root>
Now, I need to select Employee elements which are from "Account" Department. If there are none in Account then I need to pick Employee element from Finance.
How can I do that?

As an option you can use this code:
var result = XElement.Parse(xml).Descendants("Employee")
.GroupBy(x => x.Element("Department").Value)
.OrderByDescending(x=>x.Key=="Account")
.FirstOrDefault(x => (x.Key == "Account" && x.Count() > 0) ||
x.Key == "Finance").ToList();

You can do this, it is not the most elegant way. Just use || and take FirstOrDefault
var result = doc.Root.Descendants("Employee").
Where(x => x.Element("Department").Value == "Account" || x.Element("Department").Value == "Finance").
FirstOrDefault();

Combining Linq and XPath you can do it like this:
var document = XDocument.Load("data.xml").Root;
//Find a Department with a given value and retrieve its Employee parent
string xPath = "//Department[text() = '{0}']/parent::Employee";
//Search for "Account" Department. If nun was found will return null and then
//search for "Finance"
var employee = document.XPathSelectElement(string.Format(xPath, "Account")) ??
document.XPathSelectElement(string.Format(xPath, "Finance"));
If you do not want to use XPath then you can do this:
var employee = (from item in XDocument.Load("data.xml").Descendants("Employee")
let department = item.Element("Department").Value
orderby department == "Account" ? 1 :
department == "Finance" ? 2 : 3
select item).FirstOrDefault();
For all employees of those departments:
var employee = (from item in XDocument.Load("data.xml").Descendants("Employee")
group item by item.Element("Department").Value into grouping
orderby grouping.Key == "Account" ? 1 :
grouping.Key == "Finance" ? 2 : 3
select grouping.ToList()).FirstOrDefault();

Related

FirstOrDefault LINQ query with a condition

I am new to LINQ queries and want to use FirstOrDefault in my existing LINQ query.
List<vUserAll> employee = (from o in db.vUsersAll
where (o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName) && o.Domain == "dExample"
select o).ToList();
What's the correct way to do this?
This can be simplified further as:
var filtered = db.vUsersAll.FirstOrDefault(u => u. Field == filter);
If the above mentioned is the case, then you can use a labmda as in the following:
var firstOrDefaultResult = db.vUsersAll.Where(o=>
(o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName)
&& o.Domain == "dExample").FirstOrDefault()
If you want to use the same above expression then,
vUserAll employee = (from o in db.vUsersAll
where (o.sAMAccountName == modifiedAccountName || o.CN == modifiedAccountName) && o.Domain == "dExample"
select o).FirstOrDefaul();
It's a lot easier if you only use LINQ extensions. For example,
var filtered = all users.Where(u => u. Field == filter).FirstOrDefault();

Parse XML with Linq according to 2 values

I would like to know how to parse my xml file in c# with LinQ, I made a lot of research but there isn't my precise case..
So here is my xml code :
<WindowsMediaPlayer>
<Playlist name="playlistdefouf">
<Element>
<type>Audio</type>
<name>lol</name>
</Element>
<Element>
<type>Video</type>
<name>tamere</name>
</Element>
</Playlist>
</WindowsMediaPlayer>
I would like to make a function that verify if a song exists (With type AND name) according to the right playlist too.
For example if I got in parameters playlistname = "playlistdefouf",type = "Audio" and name = "lol" my function will return 1
I already tried to do something but I think I'm lost..
XDocument xmlFile = XDocument.Load(Helper.xmlFolder + "/playlist.xml");
IEnumerable<XElement> elem = xmlFile.Root.Descendants();
IEnumerable<XElement> requete = from d in elem
where d.Name == "Playlist"
&& d.Attribute("name").Value == "playlistdefouf"
select d;
IEnumerable<XElement> requete2 = from d in requete.Descendants()
where d.Name == "Element"
select d;
IEnumerable<XElement> requete3 = from d in requete2.Descendants()
select d;
Here is how to retrieve an IEnumerable of the Playlists that have a specific type and name:
XDocument xmlFile = XDocument.Load("playlists.xml");
var res = from playlist in xmlFile.Root.Elements("Playlist")
where
playlist.Attribute("name").Value == "playlistdefouf" &&
playlist.Element("Element").Element("type").Value == "Audio" &&
playlist.Element("Element").Element("name").Value == "lol"
select playlist;
You can get a count of the playlists by using the Count() extension method
res.Count();
Or you can use the Extension method Any() instead of Count to get a boolean that is more expressive if you want to know if a list contains any elements matching your parameters.
This yields the same result but I personally prefer it structured this way:
var xml = XDocument.Load("playlist.xml");
var result = from playlist in xml.Descendants("Playlist")
where (string)playlist.Attribute("name") == "playlistdefouf"
from song in playlist.Descendants("Element")
where (string)song.Element("type") == "Audio" && (string)song.Element("name") == "lol"
select playlist;
Then you can use the IEnumerable extensions to get the results you want:
var count = result.Count();
var isExisting = result.Any();
var playlist = result.ToList();

XML query - retrieving values

To my great frustration, I spent half a day to build some queries for an XML document and I don't know anything more than I knew before I started. So, I'm taking the easy way out and ask again for help.
The XML code is like this:
<?xml version="1.0" ?>
<Main>
<alpha Id = "AlphaId_1">
<beta>AlphaId1_Beta</beta>
<gamma>AlphaId1_Gama</gamma>
<delta Type = "A">AlphaId1_DeltaTypeA</delta>
<delta Type = "B">AlphaId1_DeltaTypeB</delta>
<kapa Id="01">
<description>AlphaId1_KapaId1_Descr</description>
<name>AlphaId1KapaId1_Name</name>
<teta>AlphaId1KapaId1_Teta</teta>
</kapa>
<kapa Id="02">
<description>AlphaId1KapaId2_Descr</description>
<name>AlphaId1KapaId2_Name</name>
<teta>AlhaId1KapaId2_Teta</teta>
</kapa>
</alpha>
<alpha Id = "AlphaId_2">
<beta>AlphaId2_Beta</beta>
<gamma>AlphaId2_Gama</gamma>
<delta Type = "A">AlphaId2_DeltaTypeA</delta>
<delta Type = "B">AlphaId2_DeltaTypeB</delta>
<kapa Id="01">
<description>AlphaId2_KapaId1_Descr</description>
<name>AlphaId2KapaId2_Name</name>
<teta>AlphaId2KapaId2_Teta</teta>
</kapa>
<kapa Id="02">
<description>AlphaId1KapaId2_Descr</description>
<name>AlphaId2KapaId2_Name</name>
<teta>AlhaId2KapaId2_Teta</teta>
</kapa>
</alpha>
</Main>
I am looking for a query to retrieve for example the value "AlphaId2_DeltaTypeA".
The second query should retrieve all the description values from every KapaId for a selected AlphaId.
The only code I could come up with is
XDocument xdoc = XDocument.Load(#"doc.xml");
IEnumerable<XElement> list1 = xdoc.Root.Descendants("delta");
var cifmi =
from el in list1
where (string)el.Attribute("Type") == "A"
select el;
foreach (XElement el in cifmi)
{
textBox1.AppendText(el.Value + System.Environment.NewLine);
}
The code finds two values instead of one.
var xDoc = XDocument.Load("Input.txt");
var alpha = (from a in xDoc.Root.Elements("alpha")
let deltas = a.Elements("delta")
let deltaA = deltas.First(x => (string)x.Attribute("Type") == "A")
where (string)deltaA == "AlphaId2_DeltaTypeA"
select a).First();
var descriptions = alpha.Elements("kapa")
.Select(x => (string)x.Element("description")).ToList();
It will only look for <delta> which has both Type="A" and value of AlphaId2_DeltaTypeA. If you only care about value try that one:
var alpha = (from a in xDoc.Root.Elements("alpha")
let deltas = a.Elements("delta")
where deltas.Any(x => (string)x == "AlphaId2_DeltaTypeA")
select a).First();
Update
*alpha Id == "AlphaId_1" and delta Type = "A" and the answer is AlphaId1_DeltaTypeA*
var alpha = (string)xDoc.Root
.Elements("alpha")
.First(x => (string)x.Attribute("Id") == "AlphaId_1")
.Elements("delta")
.First(x => (string)x.Attribute("Type") == "A");

Linq to xml Select statement

I'm trying to figure out how to do a select statement using linq to xml. I'd like to return the ServerTypes if the DeploymentType equals a specific value (Enterprise9999).
XML:
<Deployments>
<Deployment>
<DeploymentType>Enterprise9999</EnterpriseDeploymentType>
<Servers>
<DeploymentServer>
<ServerType>WindowsServer</ServerType>
</DeploymentServer>
<DeploymentServer>
<ServerType>LinuxServer</ServerType>
</DeploymentServer>
</Servers>
</Deployment>
<Deployment></Deployment>
<Deployment></Deployment>
</Deployments>
Here's what I have so far in the code. I'm sure I'm going about this the wrong way:
XDocument xmlDoc = XDocument.Load(#xmlFile);
IEnumerable<XElement> xlDeployments = from depRows in xmlDoc.Descendants("Deployments")
select depRows;
var deploy = xlDeployments.Descendants("Deployment");
foreach (var dep in deploy)
{
if (dep.Element("DeploymentType").ToString() == "Enterprise9999")
{
MessageBox.Show(dep.Elements("ServerType").ToString());
}
}
Is a namespace required for a select statement?
I've changed you're enclosing tag </EnterpriseDeploymentType> to </DeploymentType>
XDocument xmlDoc = XDocument.Load(#xmlFile);
var deployments = xmlDoc.Descendants("Deployment")
.Where(dep => dep.Element("DeploymentType") != null
&& dep.Element("DeploymentType").Value == "Enterprise9999");
var servers = deployments.Descendants("ServerType")
.Select(node => node.Value);
Console.WriteLine(string.Join(Environment.NewLine, servers));
prints:
WindowsServer
LinuxServer
Assuming you've corrected your XML as per my comment, you can use the following single query: -
var nodes = from n in xml.Descendants("DeploymentType")
.Where(x => x.Element("EnterpriseDeploymentType").Value.Equals("Enterprise9999"))
select n.Descendants("Servers").Descendants("ServerType").Select(s => s.Value);
Which will give you:
WindowsServer
LinuxServer

Grouping recursive child elements based on property value

Here's my example XML. How can I group these Department elements using LINQ to XML where each has the attribute hasLab='1'?
<ResearchLabs>
<Departments>
<Department hasLab="1">
<Department hasLab="0">
<Subject name="Pharma"/>
<Department hasLab="1">
<Department hasLab="0">
<Subject name="Data"/>
</Department>
</Department>
</Department>
<Department hasLab="0">
<Subject name="Submission"/>
</Department>
</Department>
</Departments>
</ResearchLabs>
Is this what you were looking for?
This groups all child departments that doesn't have a lab under parent departments that does have a lab.
var query = from dept in doc.Descendants("Department")
where (int)dept.Attribute("hasLab") == 0
&& dept.Parent.Name == "Department" // probably not needed
&& (int)dept.Parent.Attribute("hasLab") == 1
group dept by dept.Parent;
Or an alternative query, probably more efficient:
var query = from dept in doc.Descendants("Department")
where (int)dept.Attribute("hasLab") == 1
from sub in dept.Elements("Department")
where (int)sub.Attribute("hasLab") == 0
group sub by dept;
Though I'd prefer not creating a grouping (definitely more efficient):
var query = from dept in doc.Descendants("Department")
where (int)dept.Attribute("hasLab") == 1
select new
{
Parent = dept,
Children = dept.Elements("Department")
.Where(sub => (int)sub.Attribute("hasLab") == 0),
};
It's not clear exactly what you're trying to do, but you could use:
var grouped = xml.Descendants("Department")
.GroupBy(x => (int) x.Attribute("hasLab"));

Categories

Resources