By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
428,659 Members | 940 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 428,659 IT Pros & Developers. It's quick & easy.

XPath using an element value in XSL

P: n/a
Hi,

I have some XML like this:

<family>
<person name="bob">
<father ref="../../person[2]" />
</person>
<person name="charlie">
<child ref="../../person" />
</person>
</family>

When I template match on person I want to get a handle on the referenced
father element so that I can apply a template to it. Does anyone know if
this is possible and how?

E.g.
<xsl:template match="person">
<xsl:value-of select="@name"/>
<xsl:if test="count(father) != 0">
Father: <xsl:apply-templates select="eval(@ref)">
</xsl:if>
</xsl:template>

Note that the eval() function is what I'm missing.

Many thanks
Pat Turner
Jul 20 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
Note that the eval() function is what I'm missing.


it is provided as an extension function by some systems (eg saxon has a
saxon:evaluate that does this)

the pure XSLT1 way of doing this is a two stage transform, in the first
pass you write out a stylesheet that has the XPath extracted from the
source in a suitable select attribute, then you execute the generated
stylesheet.

David
Jul 20 '05 #2

P: n/a
Thanks David,

errrm, could you give me an example of what the first pass stylesheet
would look like using my example? I can't think how it would be done.

TIA
Pat
David Carlisle wrote:
Note that the eval() function is what I'm missing.

it is provided as an extension function by some systems (eg saxon has a
saxon:evaluate that does this)

the pure XSLT1 way of doing this is a two stage transform, in the first
pass you write out a stylesheet that has the XPath extracted from the
source in a suitable select attribute, then you execute the generated
stylesheet.

David

Jul 20 '05 #3

P: n/a
Pat Turner <pu************@netscape.net> writes:
Thanks David,

errrm, could you give me an example of what the first pass stylesheet
would look like using my example? I can't think how it would be done.

TIA
Pat


several ways, depending how generic/efficient you want to be.

for example

input doc (eval.xml):

<family>
<person name="bob">
<father ref="../../person[2]" />
</person>
<person name="charlie">
<child ref="../../person[1]" />
</person>
</family>
proto-stylesheet (eval1.xsl)
<xsl:stylesheet version="1.0"
xmlns:x="data:,x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>

<xsl:template x:match="father">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

<xsl:template x:match="child">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

</xsl:stylesheet>
in the above the syntax (which I just made up) is that templates
depending on a generated xpath use x:match in their match pattern, and
use x:select where they want the xpath to appear. This could be made
more efficient (i generate all templates for all dynamic xpaths which is
less code for me to write but generates more templates than needed)

Evaluation stylesheet (this has original source doc filename hardcoded,
it could be a parameter)

<xsl:stylesheet version="1.0"
xmlns:x="data:,x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>

<xsl:template x:match="father">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

<xsl:template x:match="child">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

</xsl:stylesheet>



generate real stylesheet
saxon -o eval2.xsl eval1.xsl eval.xsl

eval2.xsl has several templates expanded out. and looks like

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:x="data:,x" version="1.0">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>
<xsl:template match="father[@ref='../../person[2]']">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[2]"/>
</xsl:template>
<xsl:template match="father[@ref='../../person[1]']">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[1]"/>
</xsl:template>
<xsl:template match="child[@ref='../../person[2]']">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[2]"/>
</xsl:template>
<xsl:template match="child[@ref='../../person[1]']">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[1]"/>
</xsl:template>

</xsl:stylesheet>

run this stylesheet on original source:

$ saxon eval.xml eval2.xsl
<?xml version="1.0" encoding="utf-8"?>

Person: bob
Father: charlie

Person: charlie
Child: bob

Jul 20 '05 #4

P: n/a
Hi David,

thanks very much for the thorough working example. I see what you have
done. I guess if I wanted a generic way to provide this functionality
I'd have to write a third pass stylesheet. I.e. if I have no way of
knowing which nested elements are references of not.

FYI, I'm actually transforming a serialised graph of Java objects. This
means that elements which use references and elements which don't can
change quite easily when java code is refactored. So a generic, reusable
solution would be more pleasing.

I think it also shows that I'll be much better off using an extension. I
believe Xalan (which I am using) has such an evaluate function.

I appreciate knowing how it can be done nonetheless.

Thanks again,
Pat.

David Carlisle wrote:
Pat Turner <pu************@netscape.net> writes:

Thanks David,

errrm, could you give me an example of what the first pass stylesheet
would look like using my example? I can't think how it would be done.

TIA
Pat

several ways, depending how generic/efficient you want to be.

for example

input doc (eval.xml):

<family>
<person name="bob">
<father ref="../../person[2]" />
</person>
<person name="charlie">
<child ref="../../person[1]" />
</person>
</family>
proto-stylesheet (eval1.xsl)
<xsl:stylesheet version="1.0"
xmlns:x="data:,x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>

<xsl:template x:match="father">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

<xsl:template x:match="child">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

</xsl:stylesheet>
in the above the syntax (which I just made up) is that templates
depending on a generated xpath use x:match in their match pattern, and
use x:select where they want the xpath to appear. This could be made
more efficient (i generate all templates for all dynamic xpaths which is
less code for me to write but generates more templates than needed)

Evaluation stylesheet (this has original source doc filename hardcoded,
it could be a parameter)

<xsl:stylesheet version="1.0"
xmlns:x="data:,x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>

<xsl:template x:match="father">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

<xsl:template x:match="child">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" x:select="@ref"/>
</xsl:template>

</xsl:stylesheet>



generate real stylesheet
saxon -o eval2.xsl eval1.xsl eval.xsl

eval2.xsl has several templates expanded out. and looks like

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:x="data:,x" version="1.0">
<xsl:template mode="a" match="person">
<xsl:value-of select="@name"/>
</xsl:template>

<xsl:template match="person">
Person: <xsl:value-of select="@name"/>
<xsl:apply-templates select="father|child"/>
</xsl:template>
<xsl:template match="father[@ref='../../person[2]']">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[2]"/>
</xsl:template>
<xsl:template match="father[@ref='../../person[1]']">
Father: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[1]"/>
</xsl:template>
<xsl:template match="child[@ref='../../person[2]']">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[2]"/>
</xsl:template>
<xsl:template match="child[@ref='../../person[1]']">
Child: <xsl:value-of select="@name"/>
<xsl:apply-templates mode="a" select="../../person[1]"/>
</xsl:template>

</xsl:stylesheet>

run this stylesheet on original source:

$ saxon eval.xml eval2.xsl
<?xml version="1.0" encoding="utf-8"?>

Person: bob
Father: charlie

Person: charlie
Child: bob


Jul 20 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.