XML Get element based on attribute - c#

Can someone please help me. I'm new to XML and xElement.
How can I get number of men where year = 2013? The result should be 300.
I have this XML:
<company>
<department>
<departmentname>Dep 1</departmentname>
<year id = "2012">
<men>200</men>
<women>1000</women>
</year>
<year id = "2013">
<men>300</men>
<women>400</women>
</year>
</department>
</company>
I have this code (not working):
XElement company = XElement.Load(Server.MapPath(myXML.xml));
var men = (from a in company.Elements("department").Elements("year")
where (string)a.Attribute("id").Value == "2013"
select (string)(a.Element("men"))).ToList<string>();

What about using XPath?
var xmldoc = XDocument.Parse(#"<?xml version='1.0' encoding='utf-8'?>
<company>
<department>
<departmentname>Dep 1</departmentname>
<year id = '2012'>
<men>200</men>
<women>1000</women>
</year>
<year id = '2013'>
<men>300</men>
<women>400</women>
</year>
</department>
</company>");
Console.WriteLine(
xmldoc.XPathSelectElement(
"/company/department/year[#id='2013']/men").Value);

Assuming you want it from all departments
int mens=company.Descendants("year")
.Where(x=>x.Attribute("id").Value=="2013")
.Sum(x=>int.Parse(x.Element("men").Value));
Your query should be
int men = (from a in company.Elements("department").Elements("year")
where a.Attribute("id").Value == "2013"
select int.Parse(a.Element("men").Value)).Sum();

XElement company = XElement.Load(Server.MapPath(myXML.xml));
var txt = company.Descendants("men")
.First(x => int.Parse(x.Parent.Attribute("id").Value) == 2013).Value;
Not very robust but will do the job in your case and you should get the idea.
And with LINQ syntax rather than extension method:
var txt =
(from c in company.Descendants("men")
where c.Parent.Attribute("id").Value == "2013"
select c).First().Value;

Ok guys. I used this and it's working
Thank you for your help!
XElement com = (from p in company.Elements("department")
where (string)p.Element("departmentname").Value == "Dep 1"
select p).First();
var men= (from a in com.Elements("year")
where (string)a.Attribute("id") == "2013"
select (string)(a.Element("men"))).ToList<string>();

Related

How to get different values with same name from xml with linq

XML - Code:
<Store>
<Products>
<Product id="PROD01">
<Title>Product 1</Title>
<Description><![CDATA[Product <b>1</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Good display</Spec>
<Spec>Latest version</Spec>
</Specs>
<Availability>same day</Availability>
</Product>
<Product id="PROD02">
<Title>Product 2</Title>
<Description><![CDATA[Product <b>2</b> description]]></Description>
<Image>prod01.gif</Image>
<Specs>
<Spec>Good computer</Spec>
<Spec>Soon available</Spec>
</Specs>
<Availability>next day</Availability>
</Product>
</Products>
</Store>
C# - Code:
public List<DetailList> GetDetails()
{
DetailList d = new DetailList();
List<DetailList> DetailLists =
(from product in xdocList.Descendants("Product")
join detail in xdocDetail.Descendants("Product")
on (string)product.Attribute("id") equals (string)detail.Attribute("id")
into outerProducts
from outerProduct in outerProducts
select new DetailList
{
Detail1 = (string)product.Attribute("id"),
Detail2 = (string)product.Element("Title"),
Detail3 = (string)product.Element("Description"),
Detail4 = (string)product.Element("Image"),
Detail5 = (string)outerProduct.Elements("Specs")
Detail6 = (string)outerProduct.Element("Availability"),
Detail7 = (string)product.Element("Price"),
}).ToList();
return DetailLists;
}
Output: Good computerGood displayLatest version
But wanted output is:
Good computer
Good display
Latest version
For output I used asp:repeater. I tried to add tags like < b r/> and much more, but cant'find my mistake, how to get Spec to three different strings, not only one string. How to achive that?
I am not sure why you are joing the nodes with self, but as per your XML it is not required. You can simply project the elements like this:-
public static List<DetailList> GetDetails(XDocument xdocList)
{
DetailList d = new DetailList();
List<DetailList> DetailLists = (from product in xdocList.Descendants("Product")
select new DetailList
{
Detail1 = ((string)product.Attribute("id")),
Detail2 = ((string)product.Element("Title")),
Detail3 = ((string)product.Element("Description")),
Detail4 = ((string)product.Element("Image")),
Detail5 = product.Element("Specs")
.Elements("Spec")
.Select(x => (string)x).ToList(),
Detail6 = ((string)product.Element("Availability")),
Detail7 = ((string)product.Element("Price")),
}).ToList();
return DetailLists;
}
Since you need all the Specs separately, you should have a collection of string and not just string. So the datatype of property Detail5 should be:-
List<string> or string[]
Friend got solution from Rahul
Detail5 = String.Join("<br/>", outerProduct.Element("Specs").Elements("Spec").Select(x => (string)x).ToList()),
Thanks #Rahul Singh

Get values of specific xml elements in c# by linq

This is the result of an online xml :
<prices targetNamespace="http://api.saxo.com/v1/prices/">
<price>
<id>28924741-0-0-1-0-1-0</id>
<quantity type="integer">1</quantity>
<normalprice type="decimal">49,95</normalprice>
<price type="decimal">49,95</price>
<vatpercent type="decimal">25,00</vatpercent>
<fixedprice>false</fixedprice>
<discount type="decimal">0,00</discount>
<allowdiscount type="integer">1</allowdiscount>
<productid>28924741</productid>
<entries>
<entry>
<id>1</id>
<type type="PriceEntryType">Standard</type>
<quantity type="integer">1</quantity>
<vatpercent type="decimal">25,00</vatpercent>
<vatamount type="decimal">9,99</vatamount>
<priceunitexvat type="decimal">39,96</priceunitexvat>
<priceunitinclvat type="decimal">49,95</priceunitinclvat>
<pricetotalexvat type="decimal">39,96</pricetotalexvat>
<pricetotalinclvat type="decimal">49,95</pricetotalinclvat>
<discountpercent type="decimal">0,00</discountpercent>
<discountamountexvat type="decimal">0,00</discountamountexvat>
<discountamountinclvat type="decimal">0,00</discountamountinclvat>
</entry>
<entry>
<id>2</id>
<type type="PriceEntryType">Context</type>
<quantity type="integer">1</quantity>
<vatpercent type="decimal">25,00</vatpercent>
<vatamount type="decimal">9,99</vatamount>
<priceunitexvat type="decimal">39,96</priceunitexvat>
<priceunitinclvat type="decimal">49,95</priceunitinclvat>
<pricetotalexvat type="decimal">39,96</pricetotalexvat>
<pricetotalinclvat type="decimal">49,95</pricetotalinclvat>
<discountpercent type="decimal">0,00</discountpercent>
<discountamountexvat type="decimal">0,00</discountamountexvat>
<discountamountinclvat type="decimal">0,00</discountamountinclvat>
</entry>
<entry>
<id>3</id>
<type type="PriceEntryType">Subscription</type>
<quantity type="integer">1</quantity>
<vatpercent type="decimal">25,00</vatpercent>
<vatamount type="decimal">6,99</vatamount>
<priceunitexvat type="decimal">27,96</priceunitexvat>
<priceunitinclvat type="decimal">34,95</priceunitinclvat>
<pricetotalexvat type="decimal">27,96</pricetotalexvat>
<pricetotalinclvat type="decimal">34,95</pricetotalinclvat>
<discountpercent type="decimal">30,03</discountpercent>
<discountamountexvat type="decimal">12,00</discountamountexvat>
<discountamountinclvat type="decimal">15,00</discountamountinclvat>
</entry>
</entries>
</price>
</prices>
I tried many ways to get value of "normalprice" and "pricetotalinclvat" of last entry . but i got null or exception .
Can you guide me that how i can get those two values by using linq ?
Looks like a possible duplication of this
The short version is:
XElement element = XElement.Parse(YourString);
var prices = element.Elements("price")
.Select(item => item.Element("normalprice").Value);
These values can be extracted using Descendant in combination with Last:
var xml = XElement.Parse(xmlStr);
var normalPrice = xml
.Descendants("normalprice")
.Last()
.Value;
var pricetotalinclvat = xml
.Descendants("pricetotalinclvat")
.Last()
.Value;
If approach does not matter, loading the contents into an XDocument and accessing via XPath would seem to make more sense in this situation:
You will want to be using the System.Xml.XPath namespace with this...
System.Xml.Linq.XDocument xdoc = System.Xml.Linq.XDocument.Parse(xmlString);
decimal priceNormal = 0;
decimal.TryParse(xdoc.XPathSelectElement(#"/prices/price/normalprice").Value, out priceNormal);
decimal priceTotalInclvat = 0;
decimal.TryParse(xdoc.XPathSelectElement(#"/prices/price/entry[last()]/pricetotalinclvat").Value, out priceTotalInclvat);
You can try this:
XDocument doc = XDocument.Load(path);
var query = from price in doc.Descendants("price")
select new
{
NormalPrice = price.Element("normalprice").Value,
PriceTotalInclVat = price.Descendants("entry").Last().Element("pricetotalinclvat").Value
};
To avoid a null exception in case you don't have entries you can also do this:
var query = from price in doc.Descendants("price")
select new
{
NormalPrice = price.Element("normalprice").Value,
PriceTotalInclVat = price.Descendants("entry").Any()?price.Descendants("entry").Last().Element("pricetotalinclvat").Value:"0"
};
Or:
var query = from price in doc.Descendants("price")
let entries = price.Descendants("entry")
select new
{
NormalPrice = price.Element("normalprice").Value,
PriceTotalInclVat = entries!=null ? entries.Last().Element("pricetotalinclvat").Value : "0"
};
I tried all of the solutions in this page but i did not get any result . it is so weird .this is what i did , but it is not a good way :
var document = XDocument.Load(url);
var root = document.Root;
if (root == null)
return;
var ns = root.GetDefaultNamespace();
var mainNode = document.Element(ns + "prices");
if (mainNode == null)
return;
var priceNode = mainNode.Elements(ns + "price").FirstOrDefault().Elements();
var lastEntry = mainNode.Elements(ns + "price").FirstOrDefault().Elements().Last().Elements().Last().Elements();
foreach (var element in lastEntry.Where(element => element.Name.LocalName == "pricetotalinclvat"))
{
plusPrice = element.Value;
}
foreach (var element in priceNode.Where(xElement => xElement.Name.LocalName == "price"))
{
price = element.Value;
}
Any Suggestion to make it better ?

simple way to read xml data using linq

I have a xml structure like this. Can anyone help with a simple linq function to read this xml structure.The itemEntry node repeats according to data. I tried to read the xml using the method below,but i am getting no records in the list. Is this method here correct way to get the details...
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements("itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element("title"),
YEAR = (string)e.Element("year"),
ITEMNAME = (string)e.Element("itemname"),
CATRYLIST =
(
from p in e.Elements("categorylist").Elements("categories")
select new CATLIST
{
IDTYPE = (string)p.Element("categoryid"),
IDNUMBER = (string)p.Element("categoryName")
}).ToList()
}).ToList();
<itemslist>
<itemInformation>
<itemdate>01/23/2014</itemdate>
<itemcount>57</itemcount>
</itemInformation>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
<itemEntry>
<title>Title1</title>
<year>2013</title>
<itemname>testname</itemname>
<categorylist>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
<categories>
<categoryid>Category1</categoryid>
<categoryName>Category2</categoryName>
</categories>
</categorylist>
</itemEntry>
</itemslist>
You should try with XDocument.
XDocument xdoc = XDocument.Load("file.xml");
The System.Xml.XLinq namespace contains some awesome objects to make this easy.
var xDoc = XDocument.Parse(xml); // load your xml string, or use XDocument.Load() to load an xml file
var itemEntries = xDoc
.Root // refers to itemEntries node
.Descendants("itemEntry"); // gets all itemEntry nodes in an IEnumerable object
This gets you an IEnumerable<XNode> of all the itemEntry nodes.
From there you can do what you need, save the values to a business object, etc.
The above method works properly, i found the issue, my xml tag was having namespace attribute. i tried to get the namespace and append it with Elements while reading
XNamespace ns = xDocument.Root.Attribute("xmlns").Value;
List<CX_ITEMLIST> sList =
(from e in XDocument.Load(param.FileName).Root.Elements(ns + "itemEntry")
select new CX_ITEMLIST
{
TITLE = (string)e.Element(ns + "title"),
YEAR = (string)e.Element(ns + "year"),
ITEMNAME = (string)e.Element(ns + "itemname"),
CATRYLIST =
(
from p in e.Elements(ns + "categorylist").Elements(ns + "categories")
select new CATLIST
{
IDTYPE = (string)p.Element(ns + "categoryid"),
IDNUMBER = (string)p.Element(ns + "categoryName")
}).ToList()
}).ToList();

Not able to select relevant nested nodes: LINQ

My sample XML is something like this:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<RoleSecurity Name="A" Workflowstatus ="B">
<Accountgroup Name = "Group1">
<Attribute ID="12345" Name="Sample1"/>
<Attribute ID="12445" Name="Sample2"/>
</Accountgroup>
<Accountgroup Name = "Group2">
<Attribute ID="12345" Name="Sample1"/>
<Attribute ID="12445" Name="Sample2"/>
</Accountgroup>
</RoleSecurity>
</Root>
I am trying to enumerate and extract all the ID corresponding to a particular Role name, workflow status and account group.
My LINQ query is working to select a node based on role name. But I am unable to proceed further.
Please help!
This is my LINQ code till now.
XElement xcd = XElement.Load(strFileName);
IEnumerable<XElement> enumCust = from cust in xcd.Elements("RoleSecurity")
where (string)cust.Attribute("Name") == strRole
select cust;
Try this:
string roleName = "A";
string workflowStatus = "B";
string accountGroup = "Group1";
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Root>
<RoleSecurity Name=""A"" Workflowstatus =""B"">
<Accountgroup Name = ""Group1"">
<Attribute ID=""12345"" Name=""Sample1""/>
<Attribute ID=""12445"" Name=""Sample2""/>
</Accountgroup>
<Accountgroup Name = ""Group2"">
<Attribute ID=""12345"" Name=""Sample1""/>
<Attribute ID=""12445"" Name=""Sample2""/>
</Accountgroup>
</RoleSecurity>
</Root>";
XElement element = XElement.Parse(xml);
var ids = element.Elements("RoleSecurity")
.Where(
e =>
(string) e.Attribute("Name") == roleName &&
(string) e.Attribute("Workflowstatus") == workflowStatus)
.Elements("Accountgroup").Where(e => (string) e.Attribute("Name") == accountGroup)
.Elements("Attribute")
.Select(e => new {ID = (string) e.Attribute("ID"), Name = (string) e.Attribute("Name")});
Try with this approach, seems different from your (and in some aspects it really changes), but in my opinion its a good way to use fluently a LINQ query to parse an XML file, it follows XML node sequence and it's easy to understand:
XElement element = XElement.Load(strFileName);
var linqList = element.Elements("RoleSecurity")
.Where(entry => entry.Attribute("Name").Value == "A" &&
entry.Attribute("Workflowstatus").Value == "B")
.Descendants("Accountgroup")
.Where(x => x.Attribute("Name").Value == "Group1")
.Descendants("Attribute")
.SelectMany(id => id.Attribute("ID").Value);
XElement xcd = XElement.Load(strFileName);
IEnumerable<XElement> enumCust = from cust in xcd.Root.Elements("RoleSecurity")
where cust.Attribute("Name").Value == strRole
select cust;
This should work now
you were missing .Root which is required to enumerate below the root node
and .Value to retrive the string value of the specified attribute
refer this artical :- http://www.dotnetcurry.com/ShowArticle.aspx?ID=564
foreach (XElement xcd xelement.Descendants("Id"))
{
Console.WriteLine((string)xcd);
}

LINQ to XML Select Nodes by duplicate child node attributes

Need to pull out the Employees who have a pager.
<Employees>
<Employee>
<Name>Michael</Name>
<Phone Type="Home">423-555-0124</Phone>
<Phone Type="Work">800-555-0545</Phone>
<Phone Type="Mobile">424-555-0546</Phone>
<Phone Type="Cell">424-555-0547</Phone>
<Phone Type="Pager">424-555-0548</Phone>
</Employee>
<Employee>
<Name>Jannet</Name>
<Phone Type="Home">423-555-0091</Phone>
<Phone Type="Work">800-555-0545</Phone>
</Employee>
</Employees>
I've got LINQ to get all the Phone nodes that have a pager and I can get all the employees. I can't wrap my head aroud drilling down to the phone node but still selecting the employee node?
var data = XElement.Load(#"employee.XML" );
var whoHasPager = from teamMember in data.Elements("Employee")
where (string)teamMember.Element("Phone").Attribute("Type") == "Pager"
select teamMember;
How about this:
var whoHasPager = from teamMember in data.Elements("Employee")
where teamMember.Elements("Phone").Any(
p => p.Attribute("Type").Value == "Pager")
select teamMember;
The problem is that .Element will return the first element and not all of them of type Phone.
Try this:
var whoHasPager = from teamMember in data.Elements("Employee")
where teamMember.Elements("Phone").Any(x => x.Attribute("Type").Value == "Pager")
select teamMember;
You should see it as an SQL Statement maybe
SELECT employee from Employees where Phone Type='Pager'
therefore, the from statement should be Employees...
Note: I'm not really good in LINQ, but I think the idea is the same...
You could try using an XPath expression:
var data = XElement.Load(#"employee.XML" );
var whoHasPager =
from teamMember in data.Elements("Employee")
where teamMember.XPathSelectElement("Phone[#Type='Pager']") != null
select teamMember;
var whoHasPager = doc.XPathSelectElements(
"//Employee/Phone[#Type = 'Pager']/.."
);

Categories

Resources