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")
...
Related
I'm working with the Amazon Advertising API, which returns XML as demonstrated in this fiddle: http://xopusfiddle.net/27NxH/
I'm looking to get the value of the item as offered by Amazon, where it exists. In theory, the following should return an XML node with the name of Amount and the value of 9980
string uri = "redacted" + isbn;
string signedUri = helper.Sign(uri);
WebRequest request = HttpWebRequest.Create(signedUri);
WebResponse response = request.GetResponse();
XmlDocument xDoc = new XmlDocument();
xDoc.Load(response.GetResponseStream());
var testvar = xDoc.SelectSingleNode("/ItemLookupResponse/Items/Item/ItemAttributes/ListPrice");
However, testvar returns null. The same occurs when I attempt to return an XmlNodeList also.
I've checked that there is indeed an XML document loaded (there is), and I've noticed that the following will return the correct node (Amount) with a value of 9980:
XmlNode aznPriceNode = xDoc.DocumentElement.ChildNodes.Item(1).ChildNodes.Item(1).ChildNodes.Item(8).ChildNodes.Item(10).ChildNodes.Item(0);
However, hardcoding such a path is a terrible idea, and doesn't always work since the XML document might not always contain a ListPrice entry.
Why doesn't XPath work in this instance?
Need to add the namespace.
const string xpath = "/x:ItemLookupResponse/x:Items/x:Item/x:ItemAttributes/x:ListPrice";
XmlDocument xDoc = new XmlDocument();
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
xDoc.Load(response.GetResponseStream());
ns.AddNamespace("x", xDoc.DocumentElement.NamespaceURI);
var testvar = xDoc.SelectSingleNode(xpath, ns);
I am having a problem parsing xml that I receive from Web Service.
The xml looks very simple:
<Result xsi:schemaLocation="urn:yahoo:developer http://developer.yahooapis.com/TimeService/V1/GetTimeResponse.xsd" type="web"><Timestamp>1320677359</Timestamp></Result>
But when I try to parse it with following code I am getting no return results.
XDocument doc = XDocument.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
var datestamp = from ds in doc.Descendants("Result")
select new { currentstamp = ds.Element("Timestamp").Value };
Is there a solution or way to parse it?
Thanks you in advance
You have a couple issues: First, the Result node isn't a descendant. It's the root. Second, you ran into the most common issue when using LINQ to XML - you forgot the namespace. The following should give you what you need:
XElement doc = XElement.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
XNamespace ns = "urn:yahoo:developer";
var datestamp = from ds in doc.DescendantsAndSelf(ns + "Result")
select new { currentstamp = ds.Element(ns + "Timestamp").Value };
Note, this produces an IEnumerable. If you only want the datestamp, consider using FirstOrDefault instead. You may be able to make this simpler by just doing the following:
XElement doc = XElement.Load("http://developer.yahooapis.com/TimeService/V1/getTime?appid=StackSolution");
XNamespace ns = "urn:yahoo:developer";
var datestamp = doc.Element(ns + "Timestamp").Value;
This method avoids the namespace issue using LocalName (unqualified identifier).
var datestamp = doc.Root.Descendants().Where(c => c.Name.LocalName.Equals("Timestamp")).FirstOrDefault().FirstNode.ToString()
There is a similar question, but it seems that the solution didn't work out in my case: Weirdness with XDocument, XPath and namespaces
Here is the XML I am working with:
<?xml version="1.0" encoding="utf-8"?>
<Report Id="ID1" Type="Demo Report" Created="2011-01-01T01:01:01+11:00" Culture="en" xmlns="http://demo.com/2011/demo-schema">
<ReportInfo>
<Name>Demo Report</Name>
<CreatedBy>Unit Test</CreatedBy>
</ReportInfo>
</Report>
And below is the code that I thought it should be working but it didn't...
XDocument xdoc = XDocument.Load(#"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable());
xnm.AddNamespace(String.Empty, "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/Report/ReportInfo/Name", xnm) == null);
Does anyone have any ideas?
Thanks.
If you have XDocument it is easier to use LINQ-to-XML:
var document = XDocument.Load(fileName);
var name = document.Descendants(XName.Get("Name", #"http://demo.com/2011/demo-schema")).First().Value;
If you are sure that XPath is the only solution you need:
using System.Xml.XPath;
var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("empty", "http://demo.com/2011/demo-schema");
var name = document.XPathSelectElement("/empty:Report/empty:ReportInfo/empty:Name", namespaceManager).Value;
XPath 1.0, which is what MS implements, does not have the idea of a default namespace. So try this:
XDocument xdoc = XDocument.Load(#"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable());
xnm.AddNamespace("x", "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/x:Report/x:ReportInfo/x:Name", xnm) == null);
you can use the example from Microsoft - for you without namespace:
using System.Xml.Linq;
using System.Xml.XPath;
var e = xdoc.XPathSelectElement("./Report/ReportInfo/Name");
should do it
To work w/o default namespace suffix, I automatically expand the path.
Usage: SelectElement(xdoc.Root, "/Report/ReportInfo/Name");
private static XElement SelectElement(XElement startElement, string xpathExpression, XmlNamespaceManager namespaceManager = null) {
// XPath 1.0 does not have support for default namespace, so we have to expand our path.
if (namespaceManager == null) {
var reader = startElement.CreateReader();
namespaceManager = new XmlNamespaceManager(reader.NameTable);
}
var defaultNamespace = startElement.GetDefaultNamespace();
var defaultPrefix = namespaceManager.LookupPrefix(defaultNamespace.NamespaceName);
if (string.IsNullOrEmpty(defaultPrefix)) {
defaultPrefix = "ᆞ";
namespaceManager.AddNamespace(defaultPrefix, defaultNamespace.NamespaceName);
}
xpathExpression = AddPrefix(xpathExpression, defaultPrefix);
var selected = startElement.XPathSelectElement(xpathExpression, namespaceManager);
return selected;
}
private static string AddPrefix(string xpathExpression, string prefix) {
// Implementation notes:
// * not perfect, but it works for our use case.
// * supports: "Name~~" "~~/Name~~" "~~#Name~~" "~~[Name~~" "~~[#Name~~"
// * does not work in complex expressions like //*[local-name()="HelloWorldResult" and namespace-uri()='http://tempuri.org/']/text()
// * does not exclude strings like 'string' or function like func()
var s = Regex.Replace(xpathExpression, #"(?<a>/|\[#|#|\[|^)(?<name>\w(\w|[-])*)", "${a}${prefix}:${name}".Replace("${prefix}", prefix));
return s;
}
If anyone has a better solution to find element and attribute names, feel free to change this post.
I have the following XML:
<iq xmlns="jabber:client" to="39850777771287777738178727#guest.google.com/agsXMPP" xml:lang="en" id="sub23" from="search.google.com" type="result">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<subscription subscription="subscribed" subid="5077774B57777BD77770" node="search" jid="39850777771287777738178727#guest.google.com/agsXMPP" />
</pubsub>
</iq>
I've tried parsing with linq to sql, but it doesn't seem to understand that these are different nodes. It groups the whole iq into a single element.
Can anyone help in parsing this using XML?
The data I want to get is the subid="5077774B57777BD77770" and the id="sub23"
Thanks!
Edit:
Here's the code I have, tried doing it in two ways:
XDocument doc = XDocument.Parse("<xml>" + iq.ToString() + "</xml>");
var results = from feed in doc.Elements("xml")
select new
{
Id = (string)feed.Element("iq").Attribute("id"),
Subid = (string)feed.Element("iq").Element("pubsub").Element("subscription").Attribute("subid")
};
and
var doc = new System.Xml.XmlDocument();
doc.LoadXml(iq.ToString());
var searchId = doc.Attributes["id"];
var subid = doc.SelectSingleNode("/pubsub/subscription").Attributes["subid"];
As Dimitre pointed out, you have a namespace issue. This will work:
using System;
using System.Xml;
namespace XMLTest
{
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
namespaces.AddNamespace("ns1", "jabber:client");
namespaces.AddNamespace("ns2", "http://jabber.org/protocol/pubsub");
doc.Load("xmltest.xml");
XmlNode iqNode = doc.SelectSingleNode("/ns1:iq", namespaces);
string ID = iqNode.Attributes["id"].Value;
Console.WriteLine(ID);
XmlNode subscriptionNode = doc.SelectSingleNode("/ns1:iq/ns2:pubsub/ns2:subscription", namespaces);
string subID = subscriptionNode.Attributes["subid"].Value;
Console.WriteLine(subID);
Console.ReadLine();
}
}
}
Read this for an explanation and a complete code example how to evaluate an XPath expression that contains location steps with nodes whose names are in a default namespace and are unprefixed in the XML document..
I'm not sure if this is what you're after, but it works:
XNamespace jabber = "jabber:client";
XNamespace pubsub = "http://jabber.org/protocol/pubsub";
string xmltext = "<iq xmlns=\"jabber:client\" to=\"39850777771287777738178727#guest.google.com/agsXMPP\" xml:lang=\"en\" id=\"sub23\" from=\"search.google.com\" type=\"result\">\n"
+ "<pubsub xmlns=\"http://jabber.org/protocol/pubsub\">\n"
+ "<subscription subscription=\"subscribed\" subid=\"5077774B57777BD77770\" node=\"search\" jid=\"39850777771287777738178727#guest.google.com/agsXMPP\" />\n"
+ "</pubsub>\n"
+ "</iq>";
XDocument xdoc = XDocument.Parse(xmltext);
var iqelem = xdoc.Element(jabber + "iq");
var id = iqelem.Attribute("id").Value;
var subselem = iqelem.Element(pubsub + "pubsub").Element(pubsub + "subscription");
var subid = subselem.Attribute("subid").Value;
Console.WriteLine("SubId = {0}\nId={1}", subid, id);
I concur with Dimitre - the "empty" xmlns namespace at the top is probably causing the problem. I sometimes strip these out with a regex if they're not used, otherwise play around with XmlNameSpaceManager as described
I have XML stored in string variable:
<ItemMasterList><ItemMaster><fpartno>xxx</fpartno><frev>000</frev><fac>Default</fac></ItemMaster></ItemMasterList>
Here I want to change XML tag <ItemMasterList> to <Masterlist>. How can I do this?
System.Xml.XmlDocument and the associated classes in that same namespace will prove invaluable to you here.
XmlDocument doc = new XmlDocument();
doc.LoadXml(yourString);
XmlDocument docNew = new XmlDocument();
XmlElement newRoot = docNew.CreateElement("MasterList");
docNew.AppendChild(newRoot);
newRoot.InnerXml = doc.DocumentElement.InnerXml;
String xml = docNew.OuterXml;
I know i am a bit late, but just have to add this answer as no one seems to know about this.
XDocument doc = XDocument.Parse("<ItemMasterList><ItemMaster><fpartno>xxx</fpartno><frev>000</frev><fac>Default</fac></ItemMaster></ItemMasterList>");
doc.Root.Name = "MasterList";
Which returns the following:
<MasterList>
<ItemMaster>
<fpartno>xxx</fpartno>
<frev>000</frev>
<fac>Default</fac>
</ItemMaster>
</MasterList>
You can use LINQ to XML to parse the XML string, create a new root and add the child elements and attributes of the original root to the new root:
XDocument doc = XDocument.Parse("<ItemMasterList>...</ItemMasterList>");
XDocument result = new XDocument(
new XElement("Masterlist", doc.Root.Attributes(), doc.Root.Nodes()));
Using the XmlDocument way, you can do this as follows (and keep the tree intact):
XmlDocument oldDoc = new XmlDocument();
oldDoc.LoadXml("<ItemMasterList><ItemMaster><fpartno>xxx</fpartno><frev>000</frev><fac>Default</fac></ItemMaster></ItemMasterList>");
XmlNode node = oldDoc.SelectSingleNode("ItemMasterList");
XmlDocument newDoc = new XmlDocument();
XmlElement ele = newDoc.CreateElement("MasterList");
ele.InnerXml = node.InnerXml;
If you now use ele.OuterXml is will return: (you you just need the string, otherwise use XmlDocument.AppendChild(ele) and you will be able to use the XmlDocument object some more)
<MasterList>
<ItemMaster>
<fpartno>xxx</fpartno>
<frev>000</frev>
<fac>Default</fac>
</ItemMaster>
</MasterList>
As pointed by Will A, we can do it that way but for case where InnerXml equals the OuterXml the following solution will work out:
// Create a new Xml doc object with root node as "NewRootNode" and
// copy the inner content from old doc object using the LastChild.
XmlDocument docNew = new XmlDocument();
XmlElement newRoot = docNew.CreateElement("NewRootNode");
docNew.AppendChild(newRoot);
// The below line solves the InnerXml equals the OuterXml Problem
newRoot.InnerXml = oldDoc.LastChild.InnerXml;
string xmlText = docNew.OuterXml;