XPath Query to find Specific Element Fails - c#

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

Related

Get all attributes with the same name

I'm using XDocument and I need to parse my XML file to retrieve all attribute with the same name event if its node's name is different from the other.
For example, for this XML :
<document>
<person name='jame'/>
<animals>
<dog name='robert'/>
</animals>
</document>
I want to retrieve all attributes named 'name'.
Can I do that with one request XPath or do I need to parse every node to find thos attributes ?
Thanks for your help !
The XPath expression
//#name
will select all attributes called name, regardless of where they appear.
By the way, 'parsing' is something that happens to the XML document before XPath ever enters the picture. So when you say "do I need to parse every node", I think this isn't really what you mean. The entire document is typically already parsed before you run an XPath query. However, I'm not sure what you do mean instead of 'parse'. Probably something like "do I need to visit every element" to find those attributes? In which case the answer is no, unless in some vague implementation-dependent sense that doesn't make any difference to you.

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.

how to format SelectSingleNode xPath with index #?

i want to SelectSingleNode with index # since i have few elements with the same path.
xDoc.DocumentElement.SelectSingleNode(xPath).InnerText = xValue.ToString();
When xPath is the following string:
"/Parameter [#tag='tool_od']/Value/ValueSeries/Value[Index=1]/value"
or
"/Parameter [#tag='tool_od']/Value/ValueSeries/Value[1]/value"
or
"/Parameter [#tag='tool_od']/Value/ValueSeries/Value[#Index=1]/value"
all of those options gives me an error:
"Object reference not set to an instance of an object."
this is the part of the xml:
i want to be able to access each of childs with selectsinglenode.
<ValueSeries>
<Value>
<value>25</value>
</Value>
<Value>
<value>999012.0</value>
</Value>
<Value>
<value>999012.0</value>
</Value>
</ValueSeries>
if i will remove the index part the path will work fine but it will only access the first element and not the others.
It's hard to be sure what exactly your problem is without being able to see your input xml.
Note that you don't need to use xDoc.DocumentElement as your xpath is referring to the root node anyway (/), so you can just do xDoc.SelectSingleNode(....
If you are looking for the first "Value" element of "ValueSeries" your second xpath looks correct (does a value contain a value?), but it depends on what your xml looks like.
The "Object reference" error is due to the fact that SelectSingleNode is returning null (as your xpath is not found), and you are trying to set the property InnerText.
my error was due of using index "0", the first index is 1.

XPath query for an attribute containing forward slash (/)

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']");

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.

Categories

Resources