XPath using an element value in XSL

Discussion in 'XML' started by Pat Turner, Dec 2, 2004.

  1. Pat Turner

    Pat Turner Guest

    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
    Pat Turner, Dec 2, 2004
    #1
    1. Advertising


  2. > 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
    David Carlisle, Dec 2, 2004
    #2
    1. Advertising

  3. Pat Turner

    Pat Turner Guest

    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
    Pat Turner, Dec 2, 2004
    #3
  4. Pat Turner <> 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
    David Carlisle, Dec 2, 2004
    #4
  5. Pat Turner

    Pat Turner Guest

    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 <> 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
    >
    >
    >
    >
    >
    Pat Turner, Dec 6, 2004
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alastair Cameron
    Replies:
    1
    Views:
    7,391
    SQL Server Development Team [MSFT]
    Jul 8, 2003
  2. Replies:
    1
    Views:
    3,595
    A. Bolmarcich
    May 27, 2005
  3. AR
    Replies:
    2
    Views:
    391
    William Park
    May 27, 2005
  4. Ian Wilson
    Replies:
    2
    Views:
    550
    Ian Wilson
    Jul 26, 2007
  5. Kniffel
    Replies:
    8
    Views:
    1,969
    Kniffel
    Sep 7, 2007
Loading...

Share This Page