tags:

views:

89

answers:

2

Using only an XPath expression (and not in XSLT or DOM - just pure XPath), I'm trying to create a relative path from the current node (in a td) to an associated td in the same column of the same HTML table.

For example, suppose I have this type of data:

<table>
  <tr> <td><a>Blue Jeans</a></td> <td><a>Shirt</a></td> </tr>
  <tr> <td><span>$21.50</span></td> <td><span>$18.99</span></td> </tr>
</table>

and I'm on the a with "Blue Jeans" and want to find the price ($21.50). In XSLT, I could use the current() function to get the answer like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:apply-templates select="//a" />
    </xsl:template>

    <xsl:template match="a"> 
        Name: <xsl:value-of select="."/>
        Price: <xsl:value-of select="../../following-sibling::tr[1]/td[position() = count(current()/../preceding-sibling::td) + 1]" />
    </xsl:template>

</xsl:stylesheet>

But the problem I'm running into is that there is no current() defined in XPath 1.0. I tried using the self:: axis, but like the "." shorthand, that only points to the "context" node, not the "current" node. The language that I'm seeing in the XPath standard suggests that XPath doesn't have a concept of "current node."

Is there perhaps another way to form this path or is this a limitation of XPath?

A: 

This cannot be achieved with a single XPath 1.0 expression.

In XPath 2.0 one could write:

for $vPreceeding in count(../preceding-sibling::td)
  return ../../following-sibling::tr[1]/td[$vPreceeding]
Dimitre Novatchev
Yeah, it would be nice to have XPath 2, but I'm restricted to using PHP's built-in XPath 1.0 at the moment. I really wish the "current()" function was in XPath!
Orion Ifland
A: 

In XPath 1.0 you could do:

/table/tr/td/a[.='Blue Jeans']/following::td[count(../td)]/span

Of course, this assumes there is no colspan.

EDIT: The proof. This stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:param name="pProduct" select="'Blue Jeans'"/>
    <xsl:template match="/">
        <xsl:value-of select="/table/tr/td/a[.=$pProduct]
                                    /following::td[count(../td)]/span"/>
    </xsl:template>
</xsl:stylesheet>

Output:

$21.50

With param pProduct set to 'Shirt', output:

$18.99

Note: Of course, you need the a element in context in order to select the span element. So, with your stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:template match="text()"/>
    <xsl:template match="a">
        Name: <xsl:value-of select="."/>
        Price: <xsl:value-of select="following::td[count(../td)]/span" />
    </xsl:template>
</xsl:stylesheet>

Output:

Name: Blue Jeans
Price: $21.50
Name: Shirt
Price: $18.99
Alejandro
Ah yes, colspan would be an issue if that were there, but I don't think I have any of those in the data I'm trying to process. This answer doesn't seem to work generically for all the <a> elements...but I'm experimenting with using the "following::" axis now. (The only catch is that some of my data is riddled with tables inside of tables inside of tables...)
Orion Ifland
@Orion Ifland: *This works with any `a` element from your input*, you should test. There are not general solutions. If you have a diferent input struct then you should ask again for that or add a sample to your question.
Alejandro
I did test it with my stylesheet above and I didn't get any prices. The path you've given is an absolute path and I was looking for a relative path between the <a> element and the <span> element in the same column, something functionally equivalent to the expression I have in the xsl:value-of, but without the "current()" function.
Orion Ifland
I think I see what you're doing now - you're looking ahead through the whole document, x <td>s ahead, where x is the number of <td>s in a row.For some reason Eclipse's built-in transform (SAXON based?) didn't return results, but when I switched to using PHP, it's processor (libxslt based?) did come up with results. So I guess it doesn't work on all processors (and maybe that's a bug?) but it works in PHP, so I should be able to use this...Thanks!
Orion Ifland