XPath query for an attribute containing forward slash (/) - c#

I'm trying to do a SelectSingleNode on an XMLDocument using an XPath expression. However, when the attribute value that I'm searching contains multiple forward slashes (/), it returns null.
I can't find any resources online for escaping the forward slash. Does anyone know a way around this? Or have I got my syntax all wrong for matching the attribute value?
Example XML
<?xml version="1.0"?>
<Root>
<Page Path="/brand" />
<Page Path="/brand/armada" />
</Root>
This XPath expression returns the correct node (e.g.: the first one in the above sample)
XmlNode N = xmlDoc.SelectSingleNode("Root/Page[#Path='/brand']");
This XPath expression returns null
XmlNode N = xmlDoc.SelectSingleNode("Root/Page[#Path='/brand/armada']");
I'm in a C#, .net 3.5 environment.
EDIT: Thanks for the responses. I solved the issue by using a double forward slash in the select expression.
XmlNode N = xmlDoc.SelectSingleNode("Root//Page[#Path='/brand/armada']");

Thanks for the responses. I solved the issue by using a double forward slash in the select expression.
XmlNode N = xmlDoc.SelectSingleNode("Root//Page[#Path='/brand/armada']");

Related

Finding node in xml by attribute that starts with string

I have large xml. The xml's nodes have attribute id with valus like this: "1_32434", "2_45656". With this code:
var node = myXml.XPathSelectElement(string.Format("//*[starts-with(#id,\"{0}_\"))", someValue));
I am trying to find all nodes that have attribute id that start with "someValue_", but I get error that there is an invalid token.
There is an mismatch between opened and closed brackets, try to replace the last ')' by ']'
string.Format("//*[starts-with(#id,\"{0}_\")]", someValue)
I am not proud of this xpath. But it should give you all the nodes irregardless of starting id. If you only need for one id at a time. you should just add an ending bracket to your current xpath.
"//*[number(substring-before(#id,"_"))<10 and number(substring-after(#id,"_"))]"
some example XML would be greatly appreciated.

XPath Query to find Specific Element Fails

I am attempting to parse some XML data using XPath queries in C#. But my query is not successfully finding the element I am looking for (it finds nothing).
Whats wrong with my XPath query? Is my syntax for following-sibling incorrect or something? How can I edit my XPath to find the correct value element?
<attributes>
<real>
<name>cover rl</name>
<value>89.87414122</value>
</real>
<real>
<name>pit depth</name>
<value>2.35620671</value> <!-- This is the value I need -->
</real>
<attributes>
My XPath query that fails:
ns:attributes/real/name[text() = 'pit depth']/following-sibling::value
You're close. Mostly get rid of the spurious ns: namespace prefix. Also note that your sample input XML should end with a closing </attributes> element rather than another opening <attributes> element
So, this XPath:
/attributes/real/name[. = 'pit depth']/following-sibling::value
Will yield:
<value>2.35620671</value>
per your request.
If you only want the contents of the value element:
/attributes/real/name[. = 'pit depth']/following-sibling::value/text()
Will yield:
2.35620671

Difference between XPathEvaluate on XElement or XDocument?

Somewhere in a C# program, I need to get an attribute value from an xml structure. I can reach this xml structure directly as an XElement and have a simple xpath string to get the attribute. However, using XPathEvaluate, I get an empty array most of the time. (Yes, sometimes, the attribute is returned, but mostly it isn't... for the exact same XElement and xpath string...)
However, if I first convert the xml to string and reparse it as an XDocument, I do always get the attribute back. Can somebody explain this behavior ? (Am using .NET 3.5)
Code that mostly returns an empty IEnumerable:
string xpath = "/exampleRoot/exampleSection[#name='test']/#value";
XElement myXelement = RetrieveXElement();
((IEnumerable)myXElement.XPathEvaluate(xpath)).Cast<XAttribute>().FirstOrDefault().Value;
Code that does always work (I get my attribute value):
string xpath = "/exampleRoot/exampleSection[#name='test']/#value";
string myXml = RetrieveXElement().ToString();
XDocument xdoc = XDocument.Parse(myXml);
((IEnumerable)xdoc.XPathEvaluate(xpath)).Cast<XAttribute>().FirstOrDefault().Value;
With the test xml:
<exampleRoot>
<exampleSection name="test" value="2" />
<exampleSection name="test2" value="2" />
</exampleRoot>
By suggestion related to a surrounding root, I did some 'dry tests' in a test program, using the same xml structure (txtbxXml and txtbxXpath representing the xml and xpath expression described above):
// 1. XDocument Trial:
((IEnumerable)XDocument.Parse(txtbxXml.Text).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
// 2. XElement trial:
((IEnumerable)XElement.Parse(txtbxXml.Text).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
// 3. XElement originating from other root:
((IEnumerable)(new XElement("otherRoot", XElement.Parse(txtbxXml.Text)).Element("exampleRoot")).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
Result : case 1 and 3 produce the correct result, while case 2 throws a nullref exception.
If case 3 would fail and case 2 succeed, it would have made some sense to me, but now I don't get it...
The problem is that the XPath expression is starting with the children of the specified node. If you start with an XDocument, the root element is the child node. If you start with an XElement representing your exampleRoot node, then the children are the two exampleSection nodes.
If you change your XPath expression to "/exampleSection[#name='test']/#value", it will work from the element. If you change it to "//exampleSection[#name='test']/#value", it will work from both the XElement and the XDocument.

Finding a string inside an XmlDocument

I need to find an inner text of an element inside an XmlDocument and return it's Xpath.
for example, searching for "ThisText" inside :
<xml>
<xml2>ThisText</xml2>
</xml>
should return the Xpath of xml2
what's the most efficient way of doing this in c#?
What do you think the "xpath" of an element is? An xpath is a querying language in order to find a node/nodes, not to describe where a node is.
You can use an xpath to find the element in question. e.g.
xmlDocument.SelectNodes("//*[contains(text(), 'ThisText')]");
Then you can loop through the returned nodes and look at their name / parent, etc.

Parsing XML document with XPath, C#

So I'm trying to parse the following XML document with C#, using System.XML:
<root xmlns:n="http://www.w3.org/TR/html4/">
<n:node>
<n:node>
data
</n:node>
</n:node>
<n:node>
<n:node>
data
</n:node>
</n:node>
</root>
Every treatise of XPath with namespaces tells me to do the following:
XmlNamespaceManager mgr = new XmlNamespaceManager(xmlDoc.NameTable);
mgr.AddNamespace("n", "http://www.w3.org/1999/XSL/Transform");
And after I add the code above, the query
xmlDoc.SelectNodes("/root/n:node", mgr);
Runs fine, but returns nothing. The following:
xmlDoc.SelectNodes("/root/node", mgr);
returns two nodes if I modify the XML file and remove the namespaces, so it seems everything else is set up correctly. Any idea why it work doesn't with namespaces?
Thanks alot!
As stated, it's the URI of the namespace that's important, not the prefix.
Given your xml you could use the following:
mgr.AddNamespace( "someOtherPrefix", "http://www.w3.org/TR/html4/" );
var nodes = xmlDoc.SelectNodes( "/root/someOtherPrefix:node", mgr );
This will give you the data you want. Once you grasp this concept it becomes easier, especially when you get to default namespaces (no prefix in source xml), since you instantly know you can assign a prefix to each URI and strongly reference any part of the document you like.
The URI you specified in your AddNamespace method doesn't match the one in the xmlns declaration.
If you declare prefix "n" to represent the namespace "http://www.w3.org/1999/XSL/Transform", then the nodes won't match when you do your query. This is because, in your document, the prefix "n" refers to the namespace "http://www.w3.org/TR/html4/".
Try doing mgr.AddNamespace("n", "http://www.w3.org/TR/html4/"); instead.

Categories

Resources