nh_
nh_

Reputation: 299

XSLT 2.0 dynamic XPATH expression

I have one XML file that I need to transform based on a mapping file with XSLT 2.0. I'm using the Saxon HE processor.

My mapping file:

<element root="TEST">
    <childName condition="/TEST/MyElement/CHILD[text()='B']>/TEST/MyElement/CHILD</childName>
    <childBez condition="/TEST/MyElement/CHILD[text()='B']>/TEST/MyElement/CHILDBEZ</childBez>
</element>

I have to copy the elements CHILD and CHILDBEZ plus the parent and the root elements when the text of CHILD equals B. So with this Input:

<?xml version="1.0" encoding="UTF-8"?>
<TEST>
    <MyElement>
        <CHILD>A</CHILD>
        <CHILDBEZ>ABEZ</CHILDBEZ>
        <NotInteresting></NotInteresting>
    </MyElement>
    <MyElement>
        <CHILD>B</CHILD>
        <CHILDBEZ>BBEZ</CHILDBEZ>
        <NotInteresting2></NotInteresting2>
    </MyElement>
</TEST>

the desired output:

<TEST>
    <MyElement>
        <childName>B</childName>
        <childBez>BBEZ</childBez>
    </MyElement>
</TEST>

what I have so far (based on this solution XSLT 2.0 XPATH expression with variable):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    
<xsl:strip-space elements="*"/>

<xsl:param name="mapping" select="document('mapping.xml')"/>

<xsl:key name="map" match="*" use="."/>

<xsl:template match="/">
    <xsl:variable name="first-pass">
        <xsl:apply-templates mode="first-pass"/>
    </xsl:variable>
    <xsl:apply-templates select="$first-pass/*"/>
</xsl:template>

<xsl:template match="*" mode="first-pass">
    <xsl:param name="parent-path" tunnel="yes"/>
    <xsl:variable name="path" select="concat($parent-path, '/', name())"/>
    <xsl:variable name="replacement" select="key('map', $path, $mapping)"/>
    <xsl:variable name="condition" select="key('map', $path, $mapping)/@condition"/>        
    <xsl:choose>
        <xsl:when test="$condition!= ''">
            <!-- if there is a condition defined in the mapping file, check for it -->
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="{if ($replacement) then name($replacement) else name()}">
                <xsl:attribute name="original" select="not($replacement)"/>
                <xsl:apply-templates mode="first-pass">
                    <xsl:with-param name="parent-path" select="$path" tunnel="yes"/>
                </xsl:apply-templates>
            </xsl:element>
        </xsl:otherwise>
    </xsl:choose>

</xsl:template>

<xsl:template match="*">
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*[@original='true' and not(descendant::*/@original='false')]"/>    
</xsl:stylesheet>

but the problem is that it's impossible to evaluate dynamic XPATH expressions with XSLT 2.0. Does anyone knows a workaround for that? Plus I have a problem with the mapping file. When there is only one element in it, it's not working at all.

Upvotes: 0

Views: 760

Answers (1)

Michael Kay
Michael Kay

Reputation: 163342

If dynamic XPath evaluation isn't an option in your chosen processor, then generating an XSLT stylesheet is often a good alternative. In fact, it's often a good alternative anyway.

One way of thinking about this is that your mapping file is actually a program written in a very simple transformation language. There are two ways of executing this program: you can write an interpreter (dynamic XPath evaluation), or you can write a compiler (XSLT stylesheet generation). Both work well.

Upvotes: 1

Related Questions