10

I am currently trying the following xpath

//tr[normalize-space(td/text())='User Name']

to get all the tr that contains a td that contain 'User Name' or 'User Name' or ' User Name ' but its not working and I don't know what is wrong with the query :(
the data i want to find is in the following format

<tr><td>User Name</td></tr>
<tr><td>User     Name</td></tr>
<tr><td>  User Name   </td></tr>

So what is the right format to write this xpath query?

Edit: it seem not work if the data is in the following format

<tr><td>x</td><td>User Name</td></tr>
<tr><td>x</td><td>y</td><td>User     Name</td></tr>
<tr><td>x</td><td>y</td><td>z</td><td>  User Name   </td></tr>

so now how can i write the xpath query?
note: "//tr[normalize-space(td/text()) = 'User Name']" wont work
but "//tr/td[normalize-space(text()) = 'User Name']" will work (but i want to get the tr and not the td element)

Karim
  • 6,113
  • 18
  • 58
  • 83
  • Please show a sample of your input data, starting with ``. – Pavel Minaev Dec 01 '09 at 22:01
  • Everything looks fine at the first glance. I suspect the problem isn't with the query, but is with how you use the results. Can you show the query in context - if it's XSLT, the XSLT code that calls it and uses the results, if it's C# code directly, then that code. – Pavel Minaev Dec 01 '09 at 22:14
  • well i am using the results using c# selectnodes(). but actually i am writing the statments in the debugger window. and i am getting 0 results. seems like a data problem. will check it now. – Karim Dec 01 '09 at 22:17

2 Answers2

28

Now that you've edited the question, it makes sense. Let's consider this input:

<tr><td>x</td><td>User Name</td></tr>

and your non-working query:

//tr[normalize-space(td/text()) = 'User Name']

Now, td/text() means "select all child text nodes of all child td nodes of current node". In this case this will yield a node-set consisting two text nodes, x and User Name.

Now you call normalize-space() on that node-set. The type of the sole argument of normalize-space() is string?. Since a node-set is not a string, conversions kick in, per section 3.2 of XPath 1.0 recommendation:

An argument is converted to type string as if by calling the string() function.

Now let's look at the definition of string() in section 4.2:

A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.

In our example, the first node "in document order" is the text node x, so it is the one which will be used; the second node is ignored. Thus, you end up calling normalize-space('x'). Naturally, this won't compare equal to 'User Name'. To make this work, use:

//tr[td[normalize-space(text()) = 'User Name']]

This can be transcribed as "select all tr nodes that have child td nodes, the first child text() node of which has a normalized string value of User Name" - which is what you want. Furthermore, you can simplify this to:

//tr[td[normalize-space() = 'User Name']]

Since a no-argument normalize-space() will apply to the current node (which will be td), and process all text nodes within.

Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
1

This works fine here:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(@"
    <table>
        <tr><td>User Name</td></tr>
        <tr><td> User   Name </td></tr>
        <tr><td>   User Name   </td></tr>
    </table>");

Console.WriteLine(
    xmlDoc.SelectNodes(
        "//tr[td[normalize-space(.) = 'User Name']]").Count); // shows "3"

Can you please update your question with a actual XML sample?

Rubens Farias
  • 57,174
  • 8
  • 131
  • 162
  • the actuall file is a big one so its not possbile to upload. but the sample is like the one u have but its not working for me – Karim Dec 01 '09 at 22:13
  • you dont need to upload entire file, just small piece where you're having problems – Rubens Farias Dec 01 '09 at 22:23
  • I updated my xpath and it works with your sample data; please take a look – Rubens Farias Dec 01 '09 at 22:34
  • sorry i accepted the answer of Pavel Minaev but he has given some nice explanations. but i appreciate your answer too and yours was faster :) – Karim Dec 01 '09 at 22:49