Search code examples
xmlxsltxslt-2.0

XSLT - check node is exist or not using function


I have xml like follows,

<doc>
    <meta-data>
        <paper-name>ABC</paper-name>
        <paper-type>fi</paper-type>
    </meta-data>

    <section>
        <figure>fig1</figure>
        <figure>fig2</figure>
    </section>
</doc>

My requirement is, if <paper-type> node is available in <meta-data> change <figure> nodes to <image> node.

So, output should look like,

<doc>
    <meta-data>
        <paper-name>ABC</paper-name>
        <paper-type>fi</paper-type>
    </meta-data>

    <section>
        <image>fig1</image>
        <image>fig2</image>
    </section>
</doc>

I have written following xsl to do this task,

<xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:function name="abc:check-paper-type" as="xs:boolean">
        <xsl:sequence select="root()//meta-data/paper-type"/>
    </xsl:function>

    <xsl:template match="figure[abc:check-paper-type()]">
        <image>
            <xsl:apply-templates/>
        </image>
    </xsl:template>

to check <paper-type> node is available inside <meta-data> I have written a function here named 'check-paper-type'. but is does not work as expected.

Any suggestion how can I organize my function to check, <paper-type> is available or not?

Note that, I need to change lots of node by checking <paper-type> node exist or not. So, It'll be important to check <paper-type> exist or not by using a function.


Solution

  • The reason why your attempt cannot work is this:

    Within the body of a stylesheet function, the focus is initially undefined; this means that any attempt to reference the context item, context position, or context size is a non-recoverable dynamic error. [XPDY0002]

    http://www.w3.org/TR/xslt20/#stylesheet-functions

    You could do simply:

    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="figure[../../meta-data/paper-type]">
        <image>
            <xsl:apply-templates select="@*|node()"/>
        </image>
    </xsl:template>
    

    Given your input, this will produce:

    <?xml version="1.0" encoding="UTF-8"?>
    <doc>
       <meta-data>
          <paper-name>ABC</paper-name>
          <paper-type>fi</paper-type>
       </meta-data>
       <section>
          <image>fig1</image>
          <image>fig2</image>
       </section>
    </doc>
    

    Alternatively, if you need to refer to the existence of the node repeatedly, you can define it as a variable, instead of a function:

    <xsl:variable name="check-paper-type" select="exists(/doc/meta-data/paper-type)" />
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="figure[$check-paper-type]">
        <image>
            <xsl:apply-templates select="@*|node()"/>
        </image>
    </xsl:template>