Linq query convert to List<string> - c#

I have this code
List<string> IDs = new List<string>();
XDocument doc = XDocument.Parse(xmlFile);
var query = from c in doc.Root.Elements("a").Elements("b")
select new { ID = c.Element("val").Value};
How can I convert query to List without loop foreach ?
{ ID = c.Element("val")}
are strings of course
EDIT
my XML File
<?xml version="1.0" encoding="utf-8"?>
<aBase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>
<b>
<val>other data</val>
</b>
<b>
<val>other data</val>
</b>
</a>
</aBase>

IDs = query.Select(a => a.ID).ToList();
or if you'd like to do it in one line
List<string> IDs = (from c in doc.Root.Elements("a").Elements("b")
select c.Element("val").Value).ToList()

The anonymous type isn't really helping you since you only need a sequence of strings, not any sort of tuple. Try:
XDocument doc = XDocument.Parse(xmlFile);
var query = from c in doc.Root.Elements("a").Elements("b")
select c.Element("val").Value;
var IDs = query.ToList();
Personally, I would just use method-syntax all the way:
var IDs = doc.Root.Elements("a")
.Elements("b")
.Select(c => c.Element("val").Value)
.ToList();

Related

How can I group/sum values in LINQ to XML?

I've been trying to work thru this but I am having problems grouping the data.
I have an XML structure
<FILE>
<PLAN>
<PLAN_ID>001</PLAN_ID>
<PARTICIPANT>
<PARTICIPANT_ID>9999</PARTICIPANT_ID>
<INVESTMENTS>
<INVESTMENT>
<INVESTMENT_ID>ABC</INVESTMENT_ID>
<BALANCE>1000</BALANCE>
<ELECTION>0.00</ELECTION>
</INVESTMENT>
<INVESTMENT>
<INVESTMENT_ID>XYZ</INVESTMENT_ID>
<BALANCE>2000</BALANCE>
<ELECTION>0.00</ELECTION>
</INVESTMENT>
<INVESTMENT>
<INVESTMENT_ID>QWERTY</INVESTMENT_ID>
<BALANCE>3000</BALANCE>
<ELECTION>100.0</ELECTION>
</INVESTMENT>
</INVESTMENTS>
</PARTICIPANT>
</PLAN>
<PLAN>
<PLAN_ID>002</PLAN_ID>
<PARTICIPANT>
<PARTICIPANT_ID>9999</PARTICIPANT_ID>
<INVESTMENTS>
<INVESTMENT>
<INVESTMENT_ID>ABC</INVESTMENT_ID>
<BALANCE>2000</BALANCE>
<ELECTION>0.00</ELECTION>
</INVESTMENT>
<INVESTMENT>
<INVESTMENT_ID>XYZ</INVESTMENT_ID>
<BALANCE>4000</BALANCE>
<ELECTION>0.00</ELECTION>
</INVESTMENT>
<INVESTMENT>
<INVESTMENT_ID>QWERTY</INVESTMENT_ID>
<BALANCE>6000</BALANCE>
<ELECTION>100.0</ELECTION>
</INVESTMENT>
</INVESTMENTS>
</PARTICIPANT>
</PLAN>
</FILE>
I started with just trying to get the SUM of all of the BALANCE elements
var doc = XDocument.Load("test.xml");
var sum = (from nd in doc.Descendants("BALANCE")
select Int32.Parse(nd.Value)).Sum();
Console.WriteLine(sum);
and that worked, giving me 18000. Then I wanted to group the data by the PLAN_ID but I cannot get it to give me other than 0.
var doc = XDocument.Load("test.xml");
var q = from x in doc.Descendants("PLAN")
group x by x.Element("PLAN_ID").Value into gr
select new
{
key = gr.Key,
tot = (from tx in gr.Elements("BALANCE")
select (int)tx).Sum()
};
When I run that I get:
[0] { key = "001", tot = 0 }
[1] { key = "002", tot = 0 }
Where did I go wrong?
The problem is that you're using Elements() when the element you're looking for is deeper into the XML tree than a single level. If you switch to Descendants() within your inner query, then you should get the results you're expecting.
var doc = XDocument.Load("test.xml");
var q = from x in doc.Descendants("PLAN")
group x by x.Element("PLAN_ID").Value into gr
select new
{
key = gr.Key,
tot = (from tx in gr.Descendants("BALANCE")
select (int)tx).Sum()
};
The BALANCE nodes are 3 nodes deeper than the inner nodes of each PLAN node, so this should do the trick.
Personally, I like using the lambda version because it's a bit cleaner, so just for completeness here's the associated solution using the lambda syntax:
var q = doc.Descedants("PLAN")
.GroupBy(x => x.Element("PLAN_ID").Value))
.Select(gr => new
{
key = gr.Key,
tot = gr.Sum(tx => (int)tx.Descendants("BALANCE"))
});

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

Use where condition for retrieving the xml data with C#

I want to fetch the data from an xml file. I am fetching the id of node from the previous page. And on next page I want to display the data from xml of that id. I am passing id of node using query string but when I run my code its give me this error
System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
Here is my code
XElement xid = XElement.Parse(Request.QueryString["ID"]);
// var id = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id") == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();
Here is my xml
<?xml version="1.0" encoding="utf-8"?>
<content>
<post>
<id>1</id>
<title>fds</title>
<Discription>fdsafsdf</Discription>
<dt>21</dt>
<mnt>6</mnt>
<yr>2013</yr>
</post>
</content>
Please tell me where I am going wrong
You don't indicate the line causing the error, but I bet it's this one:
XElement xid = XElement.Parse(Request.QueryString["ID"]);
Most likely "ID" in your query string is an identifier of some sort, not XML - hence the error.
Something like this is what you want:
string xid = Request.QueryString["ID"];
Then you can use it in your where clause.
Also, there's an error in your where clause - you're trying to compare an XElement to a value - you need to get the value of the XElement using it's Value property:
Where(x => x.Element("id").Value == xid)
XElement.Value returns a string - so simply take the string value from the query string and use it in the comparison in your where clause.
Everything Put Together
string xid = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id").Value == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();

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

Reorder using Join in Linq

I want my XElements in document order.
Can I reorder my xpathGroups using Join like in this example?
XDocument message_doc = XDocument.Load(message);
var xpathGroups =
from e in contextErrors
group e by SelectElement(message_doc, e.XPath) into g
select new
{
Element = g.Key,
ErrorItems = g,
};
var documentOrderedGroups =
from elem in message_doc.Root.DescendantsAndSelf()
join e in xpathGroups on elem equals e.Element
select e;
Message:
<root>
<a>
<aa>
<aaa>9999</aaa>
</aa>
<aa>
<aaa>8888</aaa>
</aa>
</a>
<b>
<bb>
<bbb>7777</bbb>
</bb>
</b>
<c>
<cc>
<ccc>6666</ccc>
</cc>
</c>
</root>
Input data:
/root/c[1]/cc[1]/ccc[1]
/root/a[1]/aa[2]/aaa[1]
/root/b[1]/bb[1]/bbb[1]
/root/a[1]/aa[1]/aaa[1]
Expected result:
/root/a[1]/aa[1]/aaa[1]
/root/a[1]/aa[2]/aaa[1]
/root/b[1]/bb[1]/bbb[1]
/root/c[1]/cc[1]/ccc[1]
Your original queries work, and the result is an object with the element and its relevant XPath query in document order. However, the result conflicts with the comment you made that you only want the elements in document order.
Elements and XPath: if you want both the element and its XPath then the join will remain as part of the query but I would replace the grouping with a projection into an anonymous type.
var xpathElements = contextErrors.Select(e => new
{
Element = message_doc.XPathSelectElement(e.XPath),
XPath = e.XPath
});
var ordered = from e in message_doc.Descendants()
join o in xpathElements on e equals o.Element
select o;
Elements only: if you only want the elements to be in document order, the following approach would work as well.
var xpathElements = contextErrors.Select(e => message_doc.XPathSelectElement(e.XPath));
var ordered = message_doc.Descendants()
.Where(e => xpathElements.Any(o => e == o));
In both examples I've used the XPathSelectElement method to take the place of your
SelectElement method, which I gather has the same purpose.

Categories

Resources