I'm trying to parse XML string into list, result count is always zero.
string result = "";
string address = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
// Create the web request
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
// Get response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
// Read the whole contents and return as a string
result = reader.ReadToEnd();
}
XDocument doc = XDocument.Parse(result);
var ListCurr = doc.Descendants("Cube").Select(curr => new CurrencyType()
{ Name = curr.Element("currency").Value, Value = float.Parse(curr.Element("rate").Value) }).ToList();
where I'm going wrong.
The problem is that you're looking for elements without a namespace, whereas the XML contains this in the root element:
xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref"
That specifies the default namespace for any element. Also, currency and rate are attributes within the Cube elements - they're not subelements.
So you want something like:
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
var currencies = doc.Descendants(ns + "Cube")
.Select(c => new CurrencyType {
Name = (string) c.Attribute("currency"),
Value = (decimal) c.Attribute("rate")
})
.ToList();
Note that because I'm casting the currency attribute to string, you'll end up with a null Name property for any currencies which don't specify that attribute. If you want to skip those elements, you can do so with a Where clause either before or after the Select.
Also note that I've changed the type of Value to decimal rather than float - you shouldn't use float for currency-related values. (See this question for more details.)
Additionally, you should consider using XDocument.Load to load the XML:
XDocument doc = XDocument.Load(address);
Then there's no need to create the WebRequest etc yourself.
XDocument doc = XDocument.Parse(result);
XNamespace ns = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
var ListCurr = doc.Descendants(ns + "Cube")
.Where(c=>c.Attribute("currency")!=null) //<-- Some "Cube"s do not have currency attr.
.Select(curr => new CurrencyType
{
Name = curr.Attribute("currency").Value,
Value = float.Parse(curr.Attribute("rate").Value)
})
.ToList();
Related
i have a collection of service users, i want to iterate over ServiceUsers and extract value from ServiceUser, (ID, USER_NAME, UN_ID, IP, NAME)
<ServiceUsers xmlns="">
<ServiceUser>
<ID>280334</ID>
<USER_NAME>YVELAMGAMOIYENET12:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>91.151.136.178</IP>
<NAME>?????????????????????: 123456</NAME>
</ServiceUser>
<ServiceUser>
<ID>266070</ID>
<USER_NAME>ACHIBALANCE:206322102</USER_NAME>
<UN_ID>731937</UN_ID>
<IP>185.139.56.37</IP>
<NAME>123456</NAME>
</ServiceUser>
</ServiceUsers>
my Code looks like this, but i am getting null point exception.
XDocument doc = XDocument.Parse(xml)
List<XElement> xElementList = doc.Element("ServiceUsers").Descendants().ToList();
foreach (XElement element in xElementList)
{
string TEST= element.Element("Name").Value;
comboBoxServiceUser.Items.Add(element.Element("Name").Value);
}
I used the example from XmlSerializer.Deserialize Method as the base for the following snippet that reads the provided xml.
var serializer = new XmlSerializer(typeof(ServiceUsers));
ServiceUsers i;
using (TextReader reader = new StringReader(xml))
{
i = (ServiceUsers)serializer.Deserialize(reader);
}
[XmlRoot(ElementName = "ServiceUsers")]
public class ServiceUsers : List<ServiceUser>
{
}
public class ServiceUser
{
[XmlElement(ElementName = "ID")]
public string Id {get; set;}
}
I think the basis of the problem is your trailing 's' to put it short, You iterate ServiceUser not ServiceUsers
Anyway this runs through fine:
[Fact]
public void CheckIteratorTest()
{
var a = Assembly.GetExecutingAssembly();
string[] resourceNames = a.GetManifestResourceNames();
string nameOf = resourceNames.FirstOrDefault(x => x.Contains("SomeXml"));
Assert.NotNull(nameOf);
using var stream = a.GetManifestResourceStream(nameOf);
Assert.NotNull(stream);
var reader = new StreamReader(stream, Encoding.UTF8);
var serialized = reader.ReadToEnd();
var doc = XDocument.Parse(serialized);
var elemList = doc.Root.Elements("ServiceUser").ToList();
Assert.NotEqual(0, elemList.Count);
foreach(var serviceUser in elemList)
{
System.Diagnostics.Debug.WriteLine($"Name : {serviceUser.Name ?? "n.a."}");
}
}
As being said: XML is case-sensitive. Next issue is .Descendants() returns all the descendant nodes, nested ones, etc, 12 nodes in this case. So NullPointerException will happen even if you fix a "typo".
Here is your fixed code:
XDocument doc = XDocument.Parse(xml);
var xElementList = doc
.Element("ServiceUsers") // picking needed root node from document
.Elements("ServiceUser") // picking exact ServiceUser nodes
.Elements("NAME") // picking actual NAME nodes
.ToList();
foreach (XElement element in xElementList)
{
var TEST = element.Value;
Console.WriteLine(TEST); // do what you were doing instead of console
}
Use doc.Element("ServiceUsers").Elements() to get the<ServiceUser> elements. Then you can loop over the child values of those in a nested loop.
var doc = XDocument.Parse(xml);
foreach (XElement serviceUser in doc.Element("ServiceUsers").Elements()) {
foreach (XElement element in serviceUser.Elements()) {
Console.WriteLine($"{element.Name} = {element.Value}");
}
Console.WriteLine("---");
}
Prints:
ID = 280334
USER_NAME = YVELAMGAMOIYENET12:206322102
UN_ID = 731937
IP = 91.151.136.178
NAME = ?????????????????????: 123456
---
ID = 266070
USER_NAME = ACHIBALANCE:206322102
UN_ID = 731937
IP = 185.139.56.37
NAME = 123456
---
Note: Elements() gets the (immediate) child elements where as Descendants() returns all descendants. Using Elements() gives you a better control and allows you to get the properties grouped by user.
You can also get a specific property like this serviceUser.Element("USER_NAME").Value. Note that the tag names are case sensitive!
I'm trying to get "cust_name" and "code" nodes from a web API XML response below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cust_list xmlns="http://example.com">
<cust>
<cust_id>1234</cust_id>
<cust_name>abcd</cust_name>
<cust_type>
<code>2006</code>
</cust_type>
</cust>
</cust_list>
I'm writing the response as string to XMLDocument and trying to read from it. Below is my code
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://serviceURI");
request.Method = "GET";
request.ContentType = "Application/XML";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (var reader = new StreamReader(response.GetResponseStream()))
{
string responseValue = reader.ReadToEnd();
var doc = new XmlDocument();
doc.LoadXml(responseValue);
string node = doc.SelectSingleNode("/cust_list/cust/cust_name").InnerText;
string node2 = doc.SelectSingleNode("/cust_list/cust/cust_type/code").InnerText;
}
I'm trying to target specific nodes but getting "object reference not set to an instance of an object" error. what am i doing wrong here?
XElement xml = XElement.Parse(xmlString);
XNamespace ns = (string)xml.Attribute("xmlns");
var customers = xml.Elements(ns + "cust")
.Select(c => new
{
name = (string)c.Element(ns + "cust_name"),
code = (int)c.Element(ns + "cust_type")
.Element(ns + "code")
});
In this example an XElement is parsed from the input string.
A Namespace is also created using the attribute xmlns. Note how this is used when selecting elements.
All cust elements in the root element are selected and projected into a new anonymous type that currently declares a string name and an int code (you can extend this as needed).
So for example, to get the name of the first customer you could do the following:
string name = customers.First().name;
I have to process XML response from a 3rd party REST API over which I do not have control. problem is the XML response in not in proper format something like this:
<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
....contd
</Result>
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
var httpresponse = (HttpWebResponse)httprequest.GetResponse();
Person people = new Person();
List<Person> lstPerson = (from _person in xmlDoc.Document.Element("Result").Elements("Result")
select new Person
{
userName = Xdocument.Load(httpresponse.GetResponseStream()).Root.ToString(),
userEmail = _person.Element("UserEmail").Value
}).ToList();
I need to retrieve the node with value "abc" and "def" and store them in UserName which itself is root node, and also to retrieve the values in between them. so how to do this I tried various ways but was unable to to so.
Update
To create a list of your Person classes using element names for userName and the value of the UserEmail sub-element for userEmail, you can do the following:
try
{
// Load the XML from the external site
XDocument xmlDoc;
var requestUri = new Uri(uriString);
HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(requestUri);
using (var httpresponse = (HttpWebResponse)httprequest.GetResponse())
using (var stream = httpresponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
xmlDoc = XDocument.Load(reader);
}
// Extract the name & email.
var people = xmlDoc
// Get the "Result" node
.Root.Elements(xmlDoc.Root.Name.Namespace + "Result")
// Loop through its elements
.SelectMany(result => result.Elements())
// Deserialize the element name and UserEmail sub-element value as a Person
.Select(element => new Person { userName = element.Name.LocalName, userEmail = element.Element(xmlDoc.Root.Name.Namespace + "UserEmail").ValueSafe() })
.ToList();
// Process or return the list of people
}
catch (Exception ex)
{
// Handle any web exception encountered.
Debug.WriteLine(ex);
// Or rethrow if it can't be handled here
throw;
}
Using the extension method
public static class XObjectExtensions
{
public static string ValueSafe(this XElement element)
{
return element == null ? null : element.Value;
}
}
Original Answer
Your question is unclear. But if you are asking
If I have loaded an XDocument from some XML, is there a way using XmlSerializer to deserialize a portion which is an embedded list when each element of the list has a custom element name?
Then you can do this as follows:
Loading the XDocument
Navigate to the list you want to deserialize.
For each element, remember the element name, then overwrite it with the class name.
Use XElement.CreateReader() to create an XmlReader for just that element, then pass it to an XmlSerializer for deserialization.
I.e.:
// Load the document
var doc = XDocument.Parse(xml);
var people = doc
// Navigate to the list
.Root.Elements("Result")
.SelectMany(r => r.Elements())
// Deserialize each element in the list as a KeyValuePair<string, Person>
.Select(element =>
{
var name = element.Name;
element.Name = typeof(Person).DefaultXmlElementName(); // Overwrite name with name used by serializer.
using (var reader = element.CreateReader())
{
var person = (Person)new XmlSerializer(typeof(Person)).Deserialize(reader);
return new KeyValuePair<string, Person>(name.LocalName, person);
}
})
.ToList();
Given the XML string
string xml = #"<XML>
<Meta>
<Status>Success</Status>
<Debug/>
</Meta>
<Result>
<abc>
<DivisionID>tttttttttt</DivisionID>
<UserName><![CDATA[ xxx#xxxxx]]></UserName>
<UserFirstName>xxxx</UserFirstName>
<UserLastName>xxxx</UserLastName>
<UserAccountType>xxxxxxx</UserAccountType>
<UserEmail>xxxxx#xxxxx.xom</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</abc>
<def>
<DivisionID/>
<UserName><![CDATA[ xxx#xxxx]]></UserName>
<UserFirstName>yyyy</UserFirstName>
<UserLastName>vvvvvv</UserLastName>
<UserAccountType>uuuuuuuuu</UserAccountType>
<UserEmail>oooo#vvvvvv</UserEmail>
<UserAccountStatus>Active</UserAccountStatus>
</def>
</Result>
</XML>
";
Using the extension method:
public static class XmlSerializationHelper
{
public static string DefaultXmlElementName(this Type type)
{
var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
return xmlType.TypeName;
var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
return xmlRoot.ElementName;
return type.Name;
}
}
How can i parse html using Linq on a webpage and add values to a string. I am using the HtmlAgilityPack on a metro application and would like to bring back 3 values and add them to a string.
here is the url = http://explorer.litecoin.net/address/Li7x5UZqWUy7o1tEC2x5o6cNsn2bmDxA2N
I would like to get the values from the following see "belwo"
"Balance:",
"Transactions in",
"Received"
WebResponse x = await req.GetResponseAsync();
HttpWebResponse res = (HttpWebResponse)x;
if (res != null)
{
if (res.StatusCode == HttpStatusCode.OK)
{
Stream stream = res.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
html = reader.ReadToEnd();
}
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
string appName = htmlDocument.DocumentNode.Descendants // not sure what t
string a = "Name: " + WebUtility.HtmlDecode(appName);
}
}
Please try the following. You might also consider pulling the table apart as it is a little better formed than the free-text in the 'p' tag.
Cheers, Aaron.
// download the site content and create a new html document
// NOTE: make this asynchronous etc when considering IO performance
var url = "http://explorer.litecoin.net/address/Li7x5UZqWUy7o1tEC2x5o6cNsn2bmDxA2N";
var data = new WebClient().DownloadString(url);
var doc = new HtmlDocument();
doc.LoadHtml(data);
// extract the transactions 'h3' title, the node we want is directly before it
var transTitle =
(from h3 in doc.DocumentNode.Descendants("h3")
where h3.InnerText.ToLower() == "transactions"
select h3).FirstOrDefault();
// tokenise the summary, one line per 'br' element, split each line by the ':' symbol
var summary = transTitle.PreviousSibling.PreviousSibling;
var tokens =
(from row in summary.InnerHtml.Replace("<br>", "|").Split('|')
where !string.IsNullOrEmpty(row.Trim())
let line = row.Trim().Split(':')
where line.Length == 2
select new { name = line[0].Trim(), value = line[1].Trim() });
// using linqpad to debug, the dump command drops the currect variable to the output
tokens.Dump();
'Dump()', is a LinqPad command that dumps the variable to the console, the following is a sample of the output from the Dump command:
Balance: 5 LTC
Transactions in: 2
Received: 5 LTC
Transactions out: 0
Sent: 0 LTC
the document you have to parse is not the most well formed for parsing many elements are missing the class or at least id attribute but what you want to get is a second p tag
content in it
you can try this
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
var pNodes = htmlDocument.DocumentNode.SelectNodes("//p")
[1].InnerHtml.ToString().Split(new string[] { "<br />" }, StringSplitOptions.None).Take(3);
string vl="Balance:"+pNodes[0].Split(':')[1]+"Transactions in"+pNodes[1].Split(':')[1]+"Received"+pNodes[2].Split(':')[1];
I am trying to read XML from stream reader and am also getting response XML. But when i try to read its nodes it is always returning null.
var request = (HttpWebRequest) WebRequest.Create(address);
var response = (HttpWebResponse) request.GetResponse();
var stream = response.GetResponseStream();
if(stream != null)
{
var xmlReader = new XmlTextReader(stream);
var xmlDocument = new XmlDocument();
xmlDocument.Load(xmlReader);
var node = xmlDocument.SelectSingleNode("RateQuote");
}
XML Document
<RateQuoteResponse xmlns="http://ratequote.usfnet.usfc.com/v2/x1">
<STATUS>
<CODE>0</CODE>
<VIEW>SECURED</VIEW>
<VERSION>...</VERSION>
</STATUS>
<RateQuote>
<ORIGIN>
<NAME>KNOXVILLE</NAME>
<CARRIER>USF Holland, Inc</CARRIER>
<ADDRESS>5409 N NATIONAL DR</ADDRESS>
<CITY>KNOXVILLE</CITY>
<STATE>TN</STATE>
<ZIP>37914</ZIP>
<PHONE>8664655263</PHONE>
<PHONE_TOLLFREE>8006545963</PHONE_TOLLFREE>
<FAX>8656379999</FAX>
</ORIGIN>
<DESTINATION>
<NAME>KNOXVILLE</NAME>
<CARRIER>USF Holland, Inc</CARRIER>
<ADDRESS>5409 N NATIONAL DR</ADDRESS>
<CITY>KNOXVILLE</CITY>
<STATE>TN</STATE>
<ZIP>37914</ZIP>
<PHONE>8664655263</PHONE>
<PHONE_TOLLFREE>8006545963</PHONE_TOLLFREE>
<FAX>8656379999</FAX>
</DESTINATION>
<ORIGIN_ZIP>37914</ORIGIN_ZIP>
<DESTINATION_ZIP>37909</DESTINATION_ZIP>
<TOTAL_COST>99.24</TOTAL_COST>
<SERVICEDAYS>1</SERVICEDAYS>
<INDUSTRYDAYS>1.6</INDUSTRYDAYS>
<CLASSWEIGHT>
<CLASS>55</CLASS>
<ASCLASS>50</ASCLASS>
<WEIGHT>100</WEIGHT>
<CHARGES>0.0</CHARGES>
</CLASSWEIGHT>
</RateQuote>
</RateQuoteResponse>
The XML document uses the default namespace "http://ratequote.usfnet.usfc.com/v2/x1". You need to change the SelectSingleNode call to use this namespace.
You need to setup a namspace manager and then supply it to SelectSingleNode.
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("rate", "http://ratequote.usfnet.usfc.com/v2/x1");
var node = xmlDocument.SelectSingleNode("//rate:RateQuote", nsmgr);
EDIT
The RateQuoteResponse element has a default namespace xmlns="...". This means that all elements use this namespace also, unless specifically overridden.
You can remove the namespace while reading the file, just disable the namespaces on the XmlTextReader:
var request = (HttpWebRequest) WebRequest.Create(address);
var response = (HttpWebResponse) request.GetResponse();
var stream = response.GetResponseStream();
if(stream != null)
{
var xmlReader = new XmlTextReader(stream);
xmlReader.Namespaces = false;
var xmlDocument = new XmlDocument();
xmlDocument.Load(xmlReader);
var node = xmlDocument.SelectSingleNode("RateQuote");
}
After that you don't have to care about the namespace while using XPath / LINQ on your XML-elements.
The problem is that you're asking for a RateQuote element without a namespace - whereas the RateQuote element is actually in the namespace with URI http://ratequote.usfnet.usfc.com/v2/x1.
You can either use an XmlNamespaceManager to address the namespace within your XPath, or use LINQ to XML which has very simple namespace handling:
var document = XDocument.Load(stream);
XNamespace ns = "http://ratequote.usfnet.usfc.com/v2/x1";
XElement rateQuote = document.Root.Element(ns + "RateQuote");
Personally I would use LINQ to XML if you possibly can - I find it much more pleasant to use than XmlDocument. You can still use XPath if you want of course, but I personally prefer to use the querying methods.
EDIT: Note that the namespace defaulting applies to child elements too. So to find the TOTAL_COST element you'd need:
XElement cost = document.Root
.Element(ns + "RateQuote")
.Element(ns + "TOTAL_COST");
You might want to set Namespaces to false in the XmlTextReader.
So, in your code, change:
var xmlReader = new XmlTextReader(stream);
to
var xmlReader = new XmlTextReader(stream) { Namespaces = false };
With this change, you should be able to get the node you want with SelectSingleNode without having to use namespaces.
You should also be able to do:
...
var node = xmlDocument["RateQuote"];
...
The VB syntax for that is:
...
Dim node as XmlNode = xmlDocument("RateQuote")
...