Xpath, retrieving node value - c#

I get this return value from Sharepoint... which I have just included the first part of the xml snippet...
<Result ID=\"1,New\" xmlns=\"http://schemas.microsoft.com/sharepoint/soap/\">
<ErrorCode>0x00000000</ErrorCode><ID /><z:row ows_ID=\"9\"
It populates a XmlNode node object.
How using xPath can I get the value of ows_id ?
My code so far...
XmlNode results = list.UpdateListItems("MySharePointList", batch);
Update
So far I have this : results.FirstChild.ChildNodes[2].Attributes["ows_ID"].Value
But I am not sure how reliable it is, can anyone improve on it?

I don't know if its necessarily an improvement, but it might be more readable, though more verbose:
/*[local-name() = 'Result']/*[local-name() = 'row']/#ows_ID
There is probably more to the fragment you posted so this XPath query might need a fixup when used against the actual xml result.
The function, local-name(), lets you ignore namespaces, which can be both a boon and a curse. :)

When you start from root:
/Result/z:row/#ows_ID
also you can improve search if exists multiple Result:
/Result[#ID='1,New']/z:row/#ows_ID

<xsl:value-of select="Result/b:row/#ows_ID"/>
or
<xsl:value-of select="Result/b:row[#ows_ID = '9']"/>
Depending on what value you wanted

You probably need to make sure the z namespace prefix is declared correctly - that's implementation dependent. Here's how you do it in Java's XPath implementation.
Then to select the value of the ows_ID attribute, you need to navigate to the element itself, then use #ows_ID to get the value.

The specific xpath calls depend on what library you use (e.g. libxml xpath implementation).
But the generic xpath statement would be:
"//z:row[#ows_ID='9']"
This will select all z:row nodes with an attribute ows_ID of value 9.
You can modify this query to match all z:row nodes or only those with a specific attribute.
For details look here: W3Schools XPath syntax

Related

XmlDocument.SelectSingleNode omit prefixes and namespaces

This question is a follow up of the answered question:
XmlDocument.SelectSingleNode and prefix + xmlNamespace issue
The problem is that its possible that in the future the namespaces prefixes of the xml received will be changed without warning, so we would like to know if there is any way of using SelectSingleNode but omitting the prefix of an element.
(We know we could remove all the prefixes of the incoming xml but it would require more steps....albeit we would consider it a valid answer if code is provided...)
It doesn't matter if the prefix names change, as long as the namespace URIs do not change.
The prefix name you use in your code and the one in the XML document do not have to match, e.g.
namespaces.AddNamespace("foo", "http://exception.do29.imq.es/xsd");
XmlNode nodemsg = xmldocu.SelectSingleNode("//foo:message", namespaces);
Its possible to omit it using * in the xpath, for example:
//*[local-name()='ElementName']
Based on the question XPath select node with namespace
As an extra this tool was very useful to test different xpaths.... http://xpathvisualizer.codeplex.com/

How to figure out the Nth node of something that I currently am in using XPATh

Ok. I have an attribute in an xml document that I know will occur more than once. Using C# I loop through all the nodes that have this attribute. I know how to count the occurrence of an element using xpath...
count("//x/y#b")
and so on.
But is there a way that I can get the n-th value of a node that I am on... for example
<?xml version="1.0"?>
<x>
<y/>
<y/>
<y/>
</x>
Let's say I was looping through that programatically using c#. And lets say I was on the second element. Is there any way using xpath that I could figure out that I am on the 2nd node? I guess I am just trying to find my position in the iteration. Any ideas? Currently scouring the internet. If I find it out I will be sure to let you know.
Thanks.
UPDATE: CAN'T SEEM to get my stuff to work
Ok. I thought I would update my question. I can't seem to get any of your suggestions working...
<Template>
<TemplateData>
<ACOData>
<POPULATION_PATIENT_ID>6161</POPULATION_PATIENT_ID>
<PATIENT_ID>4329</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>5561</POPULATION_PATIENT_ID>
<PATIENT_ID>4327</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>6160</POPULATION_PATIENT_ID>
<PATIENT_ID>4321</PATIENT_ID>
</ACOData>
<ACOData>
<POPULATION_PATIENT_ID>5561</POPULATION_PATIENT_ID>
<PATIENT_ID>4320</PATIENT_ID>
</ACOData>
That is the XML that I am using. But I can't seem to get the correct count. I am always coming up with zero?
encounter = Int32.Parse((patElm.CreateNavigator().Evaluate("count(/Template/TemplateData/ACOData/POPULATION_PATIENT_ID[.='" + populationPatID + "']/preceding-sibling::ACOData/POPULATION_PATIENT_ID[.='"+populationPatID+"'])")).ToString());
The above is the code that I am attempting to use to get the correct value... Note my count function
count(/Template/TemplateData/ACOData/POPULATION_PATIENT_ID[.='" + populationPatID + "']/preceding-sibling::ACOData/POPULATION_PATIENT_ID[.='"+populationPatID+"'])"
To get the second such element in the document use:
(//x/y[#b])[2]
Suppose you want to go the other way. That is, you have one of these nodes and you want to know its overall position. In general, for any expression <expr> the following is true:
$n = count((<expr>)[$n]/preceding::*[count(.|<expr>)=count(<expr>)])
That is, the position of the Nth element selected by <expr> can be found by counting all the preceding elements also selected by that expression. Using similar techniques, we can find the position of some node that would be selected by a more general expression, within the set of all nodes selected by that expression.
For example, suppose we have the following document:
<x>
<y b="true"/>
<y b="true"/>
<y/>
<y/>
<x><y b="true"/><y/><y b="true">77</y></x>
<y/>
<y/>
</x>
And we want to know the position in the document of the node at /*/*/y[.='77'] among all nodes selected by //x/y[#b]. Then use the following expression:
count(/*/*/y[.='77']/preceding::*[count(.|//x/y[#b])=count(//x/y[#b])]) + 1
A more specific one-off solution looks like this:
count(/*/*/y[.='77']/preceding::y[parent::x and #b]) + 1
Result (in both cases):
4
Note: It's assumed that /*/*/y[.='77'] and (<expr>)[$n] above actually select some node in the document. If not, the result will be an erroneous 1 due to adding 1 to the result of the count. For this reason, this method is probably most useful when working on a context node or when it is guaranteed that your initial expression selects a node. (Of course, initial error checking can be employed, as well.)
Let's say I was looping through that programatically using c#. And
lets say I was on the second element. Is there any way using xpath
that I could figure out that I am on the 2nd node?
Suppose, as you say, that the current (initial context) node is /x/y[2] and you want to see what is its "position".
Evaluate this XPath expression (off the current node):
count(preceding-sibling::y) + 1
You can use the position function
x/y[position() = 3]

Declare namespaces within XPath expression

My application needs to evaluate XPath expression against some XML data. Expression is provided by user at runtime. So, I cannot create XmlNamespaceManager to pass to XPathEvaluate because I don't know prefixes and namespaces at compile time.
Is there any possibility to specify namespaces declaration within xpath expression?
Answers to comments:
XML data has one default namespace but there can be nested elements with any namespaces. User knows namespaces of the data he works with.
User-provided xpath expression is to be evaluated against many XML documents, and every document can have its own prefixes for the same namespaces.
If the same prefix can be bound to different namespaces and prefixes aren't known in advance, then the only pure XPath way to specify such expressions is to use this form of referring to elements:
someName[namespace-uri() = 'exactNamespace']
So, a particular XPath expression would be:
/*/a[namespace-uri() = 'defaultNS']/b[namespace-uri() = 'NSB']
/c[namespace-uri() = 'defaultNS']
I don't know any way to define a namespace prefix in an XPath expression.
But you can write the XPath expression to be agnostic of namespace-prefixes by using local-name() and namespace-uri() functions where appropriate.
Or if you know the XML-namespaces in advance, you can register an arbitrary prefix for them in the XmlNamespaceManager and tell your user to use that prefix in the XPath expression. It doesn't matter if the XML document itself registers a different prefix or no prefix at all. Path resolution is based on the namespace alone, not on the prefix.
Another option would be to scan the document at runtime (use XmlReader for low resource overhead if you haven't loaded it already) and then add the used mappings in the document in the XmlNamespaceManager. I'm not sure if you can get the namespaces and prefixes from XmlDocument, but I see no direct method to do it. It's easy with XmlReader though, since it exposes NamespaceURI and Prefix members for each node.
Is there any possibility to specify namespaces declaration within xpath expression?
The answer is no - it's always done in the calling environment (which is actually more flexible).
An alternative would be to use XQuery, which does allow declaring namespaces in the query prolog.
UPDATE (2020)
In XPath 3.1 you can use the syntax /*/Q{http://my-namespace}a.
Sadly, though, if you're still using Microsoft software, then the situation hasn't changed since 2011 - you're still stuck with XPath 1.0 with all its shortcomings.

LINQ to XML:Is XNode query possible

I want to use LINQ to XML in Silverlight 3 since there is no XPath support.
I have kind of got the hang of it. But the project I'm working on will not guarantee that all the XML tags I will be querying for will appear in the result XML file.
Due to this I will not be able to query the overall file as XDocument becase the absence of the tag in one document will jumble up the enumeration.
Is there anyway to typecast an XNode to XDocument? I am asking this as I am not able to query the XNode.
Even with LINQ-to-XML you should be querying by name, so I'm not sure why the absence of any particular tag should "jumble up the enumeration" - simply; you might have some nulls, i.e.
var customer = node.Element("Foo");
// now test for null ;p
You can't cast an arbitrary XNode to an XDocument, but if you are sure it is an element, casting to XElement should provide what you need.
Note also that when value nodes may be missing, you might find it easiest to use the conversion operators:
var auditDate = (DateTime?)e.Element("AuditDate");
if <AuditDate> doesn't exist, this will return an empty Nullable<DateTime> - same approach works for most common value-types, or for strings just convert to string.

How can I parse the information from this XML?

this is an example of the XML I want to scrape:
http://www.dreamincode.net/forums/xml.php?showuser=335389
Notice that the contactinformation tag has many contact elements, each similar but with different values.
For example, the element that has the AIM content in it, how can I get the content of the Value tag that's in the same family as the AIM content element?
That's where I'm stuck. Thanks!
Basically: I need to find the AIM content tag, make a note of where it is, and find the Value element within that same family. Hope this makes the question clearer
LINQToXML
var doc = XDocument.Load(#"http://www.dreamincode.net/forums/xml.php?showuser=335389");
var aimElements = doc.Descendants("contact").Where(a=>a.Element("title").Value == "AIM").Select(a=>a.Element("value").Value);
this will give you a list of strings that hold the value of the value element for a contact that has the title AIM, you can do a First() or a FirstOrDefault if you believe there should only be 1
Using an xpath like the one below will get you the contact/value node where contact/title is "AIM":
/ipb/profile/contactinformation/contact[title='AIM']/value
Have you tried to parse the XML rather than "scraping" it?

Categories