XPath using an element value in XSL

P

Pat Turner

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
 
D

David Carlisle

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
 
P

Pat Turner

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
 
D

David Carlisle

Pat Turner said:
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
 
P

Pat Turner

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 said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top