Parse XML with Linq according to 2 values - c#

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

Related

Conditional Output by Linq to XML

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

XML to Linq C#, no results

I'm trying to grab the Filename from the below XML code where the partNum passed in by the end user matches the partNum of a JES.
My current code yields 0 results.
I will also be trying to grab the disrete attribute as well.
<JESs>
<JES partNum="116102440002" discrete="true">
<Filename>116-10244-0002_ILLK Collimator Cover Assy_Rev 3.docx</Filename>
</JES>
<JES partNum="116102440003" discrete="false">
<Filename>ILLK Collimator in Gimbal_Rev 4.docx</Filename>
</JES>
<JES partNum="116102440004" discrete="true">
<Filename>116-10244-0004_Collimator Cover Installation_Rev 1.docx</Filename>
</JES>
<JES partNum="116102440005" discrete="true">
<Filename>116-10244-0005_Collimator Lens Assembly_Rev 2.docx</Filename>
</JES>
</JESs>
C# Code
var FileName = (from n in xml.Descendants("JESs") where n.Element("JES").Attribute("partNum").Value == Convert.ToString(partNum) select n.Elements().Descendants().Elements()).ToList();
You can simplify (and fix) your query like this:
var partNumber = Convert.ToString(partNum);
var result = xml.Descendants("JES")
.FirstOrDefault(x => (string)x.Attribute("partNum") == partNumber);
if(result != null)
{
var fileName = (string)result.Element("Filename");
}
Shouldn't it be:
var FileNames = (from n in xml.Descendants("JESs")
where n.Element("JES").Attribute("partNum").Value == Convert.ToString(partNum)
select n.Element("JES").Element("Filename")).ToList();
Try this
var fileName = (from x in xml.Elements()
where x.Attribute("partNum").Value == "xxxx"
select x.Element("Filename").Value).FirstOrDefault();
//replace xxxx with actual value
If no elements found for given partNum, fileName will be null

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

Dynamic XML Sorting using LINQ

How can I sort dynamic XML using LINQ having following precedences:
Sort by node-name
Sort by node-value
Sort by attribute-name
Sort by attribute-value
Sorting by Node Name:
var doc = XDocument.Parse("<data><carrot /><apple /><orange /></data>");
var sortedByNames = doc.Root.Elements().OrderBy(e => e.Name.ToString());
foreach(var e in sortedByNames)
Console.WriteLine (e.Name);
Sorted by Node Value:
var doc = XDocument.Parse("<data><thing>carrot</thing><thing>apple</thing><thing>orange</thing></data>");
var sortedByValue = doc.Root.Elements().OrderBy(e => e.Value.ToString());
foreach(var e in sortedByValue)
Console.WriteLine (e.Value);
It all follows the same pattern... You sort based on the criteria you define in the selector function passed into the OrderBy method.
var data = from item in xmldoc.Descendants("content")
orderby (string)item.Element("title") // by node value
//orderby item.Attribute("something") // by attribute value
select new
{
Title = (string)item.Element("title"),
};

Categories

Resources