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);
Related
I have xml as a response, need to find marked red arrow node:
My code:
//response to xmlDocument
document = new XmlDocument();
document.LoadXml(response.Content);
XmlNamespaceManager ns = new XmlNamespaceManager(document.NameTable);
foreach (XmlAttribute curAttribute in document.DocumentElement.Attributes)
{
if (curAttribute.Prefix.Equals("xmlns"))
{ ns.AddNamespace(curAttribute.LocalName, curAttribute.Value); }
}
string xpath = "//edmx:Edmx/edmx:DataServices/Schema[#Namespace='Core.Entities']/EntityType[#Name='Office']/Property[#Name='OfficeKeyNumeric']";
XmlNode node = document.SelectSingleNode(xpath, ns);
}
I have an error that node can't be found by given XPath, node is null.
What I tried:
with and
string xpath = "//edmx:Edmx/edmx:DataServices/Schema[#Namespace='Core.Entities' and #xmlns='http://docs.oasis-open.org/odata/ns/edm']/EntityType[#Name='Office']/Property[#Name='OfficeKeyNumeric']";
without and
string xpath = "//edmx:Edmx/edmx:DataServices/Schema[#Namespace='Core.Entities'][#xmlns='http://docs.oasis-open.org/odata/ns/edm']/EntityType[#Name='Office']/Property[#Name='OfficeKeyNumeric']";
Tried also with pipe |, & - nothing helped.
Why it doesn't work and is it possible to make it work in that way?
The only one working solution I'm using now is to remove xmlns="http://docs.oasis-open.org/odata/ns/edm"from XML document before loading, after that my code above works fine.
document.LoadXml(response.Content.Replace("xmlns=\"http://docs.oasis-open.org/odata/ns/edm\"", ""));
The Schema element and its descendants are declared in the http://docs.oasis-open.org/odata/ns/edm namespace, which must be referenced in the
xpath statement you are looking for.
string xpath = "//edmx:Edmx/edmx:DataServices/edm:Schema[#Namespace='Core.Entities']/edm:EntityType[#Name='Office']/edm:Property[#Name='OfficeKeyNumeric']";
Make sure to have your XmlNamespaceManager initialized with these namespaces.
XmlNamespaceManager ns = new XmlNamespaceManager(document.NameTable);
ns.AddNamespace("edmx","http://docs.oasis-open.org/odata/ns/edmx");
ns.AddNamespace("edm","http://docs.oasis-open.org/odata/ns/edm");
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")
...
Having problems getting NodeList.SelectSingleNode() to work properly.
My XML looks like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<inm:Results xmlns:inm="http://www.namespace.com/1.0">
<inm:Recordset setCount="18254">
<inm:Record setEntry="0">
<!-- snip -->
<inm:Image>fileName.jpg</inm:Image>
</inm:Record>
</inm:Recordset>
</inm:Results>
The data is a long series of <inm:Record> entries.
I open the doc and get create a NodeList object based on "inm:Record". This works great.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNodeList xRecord = xdoc.GetElementsByTagName("inm:Record");
I start looping through the NodeList using a for loop. Before I process a given entry, I want to check and see if the <inm:Image> is set. I thought it would be super easy just to do
string strImage = xRecord[i].SelectSingleNode("inm:Image").InnerText;
My thinking being, "For the XRecord that I'm on, go find the <inm:Image> value ...But this doesn't work as I get the exception saying that I need a XmlNameSpaceManager. So, I tried to set that up but could never get the syntax right.
Can someone show me how to use the correct XmlNameSpaceManager syntax in this case.
I've worked around the issue for now by looping through all of the childNodes for a given xRecord, and checking the tag once I loop around to it. I would like to check that value first to see if I need to loop over that <inm:Record> entry at all.
No need to loop through all the Record elements, just use XPath to specify the subset that you want:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(openFileDialog1.FileName);
XmlNamespaceManager manager = new XmlNamespaceManager(xdoc.NameTable);
manager.AddNamespace("inm", "http://www.inmagic.com/webpublisher/query");
XmlNodeList nodes = xdoc.SelectNodes("/inm:Results/inm:Recordset/inm:Record[inm:Image != '']", manager);
Using the LINQ to XML libraries, here's an example for retrieving that said node's value:
XDocument doc = XDocument.Load(openFileDialog1.FileName);
List<XElement> docElements = doc.Elements().ToList();
XElement results = docElements.Elements().Where(
ele => ele.Name.LocalName == "Results").First();
XElement firstRecord = results.Elements().Where(
ele => ele.Name.LocalName == "Record").First();
XElement recordImage = firstRecord .Elements().Where(
ele => ele.Name.LocalName == "Image").First();
string imageName = recordImage.Value;
Also, by the way, using Hungarian notation for a type-checked language is overkill. You don't need to prepend string variables with str when it will always be a string.
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
string strImage = xRecord[i].SelectSingleNode("inm:Image",nsMgr).InnerText;
Should do it.
Using this Xml library, you can get all the records that have an Image child element with this:
XElement root = XElement.Load(openFileDialog1.FileName);
XElement[] records = root.XPath("//Record[Image]").ToArray();
If you want to be sure that the Image child contains a value, it can be expressed like this:
XElement[] records = root.XPath("//Record[Image != '']").ToArray();
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;
My code doesn't return the node
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNode node_ = xml.SelectSingleNode(node);
return node_.InnerText; // node_ = null !
I'm pretty sure my XML and Xpath are correct.
My Xpath : /ItemLookupResponse/OperationRequest/RequestId
My XML :
<?xml version="1.0"?>
<ItemLookupResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
<OperationRequest>
<RequestId>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</RequestId>
<!-- the rest of the xml is irrelevant -->
</OperationRequest>
</ItemLookupResponse>
The node my XPath returns is always null for some reason. Can someone help?
Your XPath is almost correct - it just doesn't take into account the default XML namespace on the root node!
<ItemLookupResponse
xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
*** you need to respect this namespace ***
You need to take that into account and change your code like this:
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xml.NameTable);
nsmgr.AddNamespace("x", "http://webservices.amazon.com/AWSECommerceService/2005-10-05");
XmlNode node_ = xml.SelectSingleNode(node, nsmgr);
And then your XPath ought to be:
/x:ItemLookupResponse/x:OperationRequest/x:RequestId
Now, your node_.InnerText should definitely not be NULL anymore!
Sorry for the late reply but I had a similar problem just a moment ago.
if you REALLY want to ignore that namespace then just delete it from the string you use to initialise the XmlDocument
text=text.Replace(
"<ItemLookupResponse xmlns=\"http://webservices.amazon.com/AWSECommerceService/2005-10-05\">",
"<ItemLookupResponse>");
XmlDocument xml = new XmlDocument();
xml.InnerXml = text;
XmlNode node_ = xml.SelectSingleNode(node);
return node_.InnerText;