Linq to XML conditioning attribute - c#

This is my xml
<DataSet xmlns="http://www.bnr.ro/xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bnr.ro/xsd nbrfxrates.xsd">
<Header>
<Publisher>National Bank of Romania</Publisher>
<PublishingDate>2016-03-24</PublishingDate>
<MessageType>DR</MessageType>
</Header>
<Body>
<Subject>Reference rates</Subject>
<OrigCurrency>RON</OrigCurrency>
<Cube date="2016-03-24">
<Rate currency="EUR">4.4655</Rate>
</Cube>
<Cube date="2016-03-23">
Rate currency="EUR">4.4641</Rate>
</Cube>
</Body>
</DataSet>
I want to verify the Cube Attribute date to receive the EUR value from yesterday date.
For example if today is 2016-03-24 I want to receive the value 4.4641 from 2016-03-23.
I tried with LINQ to XML
string date_yesterday = DateTime.Now.AddDays(-1).Date.ToString("yyyy-MM-dd");
XElement root = XElement.Parse(sbXmlText.ToString());
IEnumerable<XElement> adress =
from el in root.Descendants("Cube")
let z = el.ElementsAfterSelf().FirstOrDefault()
where z != null && (string)el.Attribute("date") == date_yesterday
select el;
foreach (XElement el in adress)
Console.WriteLine(el);
And tried
string date_yesterday = DateTime.Now.AddDays(-1).Date.ToString("yyyy-MM-dd");
XElement root = XElement.Parse(sbXmlText.ToString());
IEnumerable<XElement> adress =
root.Descendants("Cube").Where(r => r.Attribute("date").Value == date_yesterday);
foreach (XElement el in adress)
Console.WriteLine(el);
And it returns everytime null

Your XML has default namespace. You can use "XNamespace+element's local-name" to reference element in namespace, for example :
var xml = #"<DataSet xmlns='http://www.bnr.ro/xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://www.bnr.ro/xsd nbrfxrates.xsd'>
<Header>
<Publisher>National Bank of Romania</Publisher>
<PublishingDate>2016-03-24</PublishingDate>
<MessageType>DR</MessageType>
</Header>
<Body>
<Subject>Reference rates</Subject>
<OrigCurrency>RON</OrigCurrency>
<Cube date='2016-03-24'>
<Rate currency='IDR'>1.1111</Rate>
<Rate currency='EUR'>4.4655</Rate>
</Cube>
<Cube date='2016-03-23'>
<Rate currency='EUR'>4.4641</Rate>
</Cube>
</Body>
</DataSet>";
var doc = XDocument.Parse(xml);
//XNamespace that reference default namespace URI:
XNamespace d = "http://www.bnr.ro/xsd";
var yesterday = DateTime.Now.AddDays(-1).Date;
//Use `XNamespace+element's local-name` to reference element in namespace:
var result = (from cube in doc.Descendants(d+"Cube")
from rate in cube.Elements(d+"Rate")
where
((DateTime)cube.Attribute("date")).Date == yesterday
&&
(string)rate.Attribute("currency") == "EUR"
select (decimal)rate
).FirstOrDefault();
Console.WriteLine(result);
output :
4.4641

Related

Get specific node from Xml usin Linq

I want to get the specific node from my XML response using Linq
My Xml:
<DataSet xmlns="http://www.bnr.ro/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bnr.ro/xsd nbrfxrates.xsd">
<Header>
<Publisher>National Bank of Romania</Publisher>
<PublishingDate>2016-12-30</PublishingDate>
<MessageType>DR</MessageType>
</Header>
<Body>
<Subject>Reference rates</Subject>
<OrigCurrency>RON</OrigCurrency>
<Cube date="2016-01-04">
<Rate currency="AED">1.1272</Rate>
<Rate currency="EUR">4.5169</Rate>
<Rate currency="BGN">2.3094</Rate>
<Rate currency="HUF" multiplier="100">1.4320</Rate>
<Rate currency="INR">0.0622</Rate>
<Rate currency="JPY" multiplier="100">3.4798</Rate>
<Rate currency="KRW" multiplier="100">0.3481</Rate>
<Rate currency="MDL">0.2107</Rate>
</Cube>
<Cube>
...
</Cube>
</Body>
</DataSet>
So i want to position on the cube which have date equals with a date paramater. Then i want to gate the rate value which has currency equals with "EUR".
I am trying to do this with Linq but it's not working
My Linq code:
WebClient webClient = new WebClient();
string url = "http://www.bnr.ro/files/xml/years/nbrfxrates" + year + ".xml";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
XDocument systemXml = XDocument.Load(response.GetResponseStream());
XElement cube = (from cubeElement in systemXml.Elements("Cube")
where cubeElement.Attribute("date").ToString().Equals(data)
select cubeElement).Single();
XElement rate = (from rateElement in cube.Elements("Rate")
where rateElement.Attribute("currency").ToString().Equals("EUR")
select rateElement).Single();
My problem is that systemXml.Elements("Cube") returns null.
This is my url for web request http://www.bnr.ro/files/xml/years/nbrfxrates2017.xml
It looks like you need the namespace.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication55
{
class Program
{
static void Main(string[] args)
{
string url = "http://www.bnr.ro/files/xml/years/nbrfxrates2017.xml";
XDocument systemXml = XDocument.Load(url);
XNamespace ns = ((XElement)systemXml.FirstNode).GetDefaultNamespace();
DateTime date = DateTime.Parse("2017-01-05");
var results = systemXml.Descendants(ns + "Cube")
.Where(x => ((DateTime)x.Attribute("date") == date))
.Descendants(ns + "Rate")
.Where(x => (string)x.Attribute("currency") == "EUR")
.FirstOrDefault();
var value = (decimal)results;
}
}
}
Your Root element is DataSet,which has childs and one of them is Body,which has childs Cube. Also you need to specify the namespace of the element which is {http://www.bnr.ro/xsd}. So here is working LINQ for you.
XDocument systemXml = XDocument.Load(path);
XElement cube = (from cubeElement in systemXml.Elements("{http://www.bnr.ro/xsd}DataSet").Elements("{http://www.bnr.ro/xsd}Body").Elements("{http://www.bnr.ro/xsd}Cube")
where cubeElement.Attribute("date").Value.Equals("2017-01-03")
select cubeElement).Single();
XElement rate = (from rateElement in cube.Elements("{http://www.bnr.ro/xsd}Rate")
where rateElement.Attribute("currency").Value.Equals("EUR")
select rateElement).Single();
Enjoy it!!

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 ?

How to convert xml string to an object using c#

I am using WebRequest and WebReponse classes to get a response from a web api. The response I get is an xml of the following format
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<A></A>
<B></B>
<C></C>
<D>
<E NAME="aaa" EMAIL="a#a.com"/>
<E NAME="bbb" EMAIL="b#b.com"/>
</D>
</ROOT>
I want to get all the E elements as a List<E> or something.
Can some one guide me on this pls.
if you want to avoid serialization, as you only want a very specific part of the xml, you can do this with one LINQ statement:
var items = XDocument.Parse(xml)
.Descendants("E")
.Select(e => new
{
Name = e.Attribute("NAME").Value,
Email = e.Attribute("EMAIL").Value
})
.ToList();
Working example:
var doc = XDocument.Parse(#"<?xml version='1.0' encoding='UTF-8'?>
<ROOT>
<A></A>
<B></B>
<C></C>
<D>
<E NAME='aaa' EMAIL='a#a.com'/>
<E NAME='bbb' EMAIL='b#b.com'/>
</D>
</ROOT>");
var elements = from el in doc.Elements()
from el2 in el.Elements()
from el3 in el2.Elements()
where el3.Name == "E"
select el3;
foreach (var e in elements)
{
Console.WriteLine(e);
}

Get Element from XDocument & Edit Attribute

<GetPromotionByIdResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="2" xmlns="http://fake.com/services">
<Header>
<Response ResponseCode="OK">
<RequestID>1</RequestID>
</Response>
</Header>
<Promotion PromotionId="5200" EffectiveDate="2014-10-10T00:00:00" ExpirationDate="2014-11-16T23:59:00" PromotionStatus="Active" PromotionTypeName="DefaultPromotion">
<Description TypeCode="Long" Culture="en-AU">Promotion Description</Description>
</Promotion>
</GetPromotionByIdResponse>
Im trying to extract this
<Promotion PromotionId="5200" EffectiveDate="2014-10-10T00:00:00" ExpirationDate="2014-11-16T23:59:00" PromotionStatus="Active" PromotionTypeName="DefaultPromotion">
<Description TypeCode="Long" Culture="en-AU">Promotion Description</Description>
</Promotion>
and convert the PromotionId="5200" to PromotionId="XXX"
I can extract the < Promotion > element with the below code but cant work out how to change the attribute
XNamespace xmlResponseNamespace = xmlPromotionResponse.Root.GetDefaultNamespace();
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("def", xmlResponseNamespace.ToString());
XElement xmlPromotionElement =
xmlPromotionResponse
.Descendants().SingleOrDefault(p => p.Name.LocalName == "Promotion");
You can try this way :
XNamespace ns = "http://fake.com/services";
XElement xmlPromotionElement = xmlPromotionResponse.Descendants(ns+"Promotion")
.SingleOrDefault();
xmlPromotionElement.Attribute("PromotionId").Value = "XXX";
Use simple XNamespace + local-name to reference an element in namespace. Then you can use .Attribute() method to get XAttribute from an XElement and change the attribute's value.
Try this : It returns the value of all attributes in Promotion Tag.
XNamespace ns1 = XNamespace.Get("http://fake.com/services");
var readPromotion = from a in xx.Descendants(ns1 + "Promotion")
select new
{
PromotionID = (string)a.Attribute("PromotionId"),
EffectiveDate = (string)a.Attribute("EffectiveDate"),
ExpirationDate = (string)a.Attribute("ExpirationDate"),
PromotionStatus = (string)a.Attribute("PromotionStatus"),
PromotionTypeName = (string)a.Attribute("PromotionTypeName"),
Description = (string)a.Value
};
foreach (var read in readPromotion)
{
// Read values
}

Reading remote XML in C#

I'm reading a remote XML file and once the XML is loaded into an XMLDocument object I need to traverse through it and extract the values that my application requires. My code is as follows:
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load("http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml");
XmlNamespaceManager nsMan = new XmlNamespaceManager(xmlDocument.NameTable);
nsMan.AddNamespace("gesmes", "http://www.gesmes.org/xml/2002-08-01");
nsMan.AddNamespace("", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
XmlNodeList xmlNodeList = xmlDocument.DocumentElement.SelectNodes("/gesmes:Envelope/Cube/Cube/Cube", nsMan);
HttpContext.Current.Response.Write("The numner of nodes is " + xmlNodeList.Count); //it's always zero
However the problem I'm getting is that XmlNodeList always returns zero nodes, whereas if I evaluate the XPath expression in XMLSpy I get the nodes I require.
For reference the XML looks like:
<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time='2011-07-27'>
<Cube currency='USD' rate='1.4446'/>
<Cube currency='GBP' rate='0.88310'/>
</Cube>
</Cube>
</gesmes:Envelope>
I want to return the Cube nodes for USD and GBP.
Any ideas you clever people?
Thanks
Al
While you definitely can work with namespaces and XPath in the XmlDocument API, I would strongly advise you to use LINQ to XML (.NET 3.5) if at all possible:
string url = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";
XDocument doc = XDocument.Load(url);
XNamespace gesmes = "http://www.gesmes.org/xml/2002-08-01";
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
var cubes = doc.Descendants(ns + "Cube")
.Where(x => x.Attribute("currency") != null)
.Select(x => new { Currency = (string) x.Attribute("currency"),
Rate = (decimal) x.Attribute("rate") });
foreach (var result in cubes)
{
Console.WriteLine("{0}: {1}", result.Currency, result.Rate);
}

Categories

Resources