XSLT 2.0 XPATH expression with variable

2019-09-06 00:23发布

问题:

I'm working on a Java based xsl-transformation (XSLT 2.0, could also be XSLT 3.0 if there is a free processor for java) with different input xml files. one input file could look like this:

<?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>

I want to copy all elements but "NotInteresting" and rename the two nodes CHILD and CHILDBEZ based on two parameters that I get from a mapping file:

  • the xpath expression that tells me where the text of interest is placed (in this case: TEST/MyFirstElement/CHILD and TEST/MyFirstElement/CHILDBEZ)
  • and the names of the elements what they should have in the output file (in this case: childName and childBez)

the mapping file:

<?xml version="1.0" encoding="UTF-8"?>
<element root="TEST">
    <childName>TEST/MyElement/CHILD</childName>
    <childBez>TEST/MyElement/CHILDBEZ</childBez>
</element>

desired output:

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

what I have so far:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0 "
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.apoverlag.at"
    xmlns:apo="http://www.apoverlag.at">

    <xsl:variable name="vars" select="document('mapping.xml')"/>
    <xsl:param name="src" />
     <xsl:variable name="path" xpath-default-namespace="" select="$src/path"/> <!-- = TEST/*-->

    <xsl:template match="/">
        <xsl:for-each select="$path">
            <xsl:call-template name="buildNode">
                <xsl:with-param name="currentNode" select="current()"></xsl:with-param>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>


    <xsl:template name="buildNode">
        <xsl:param name="currentNode" />
        <xsl:element name="test">
            <xsl:for-each select="$vars/element/@root">
                <xsl:for-each select="$vars/element/*">
                    <xsl:element name="{name(.)}"> <xsl:value-of select="concat($currentNode,'/',current())" /> 
                        </xsl:element>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:transform>

My problem is that:

<xsl:value-of select="concat($currentNode,'/',current())" /> 

gives me "/TEST/MyFirstElement/CHILD" when I try it hardcoded with:

<xsl:value-of select="$currentNode/CHILD" /> 

I receive my desired output. Can anyone give me a hint how to solve this problem?

回答1:

I would suggest a radically different approach. To simplify, I have used a mapping document with full paths (starting with the / root node):

mapping xml

<element root="TEST">
    <childName>/TEST/MyElement/CHILD</childName>
    <childBez>/TEST/MyElement/CHILDBEZ</childBez>
</element>

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<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"/>
    <xsl:variable name="path" select="concat($parent-path, '/', name())" />
    <xsl:variable name="replacement" select="key('map', $path, $mapping)" />
    <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"/>
        </xsl:apply-templates>
    </xsl:element>
</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>

Result, when applied to the provided input:

<?xml version="1.0" encoding="UTF-8"?>
<TEST>
   <MyElement>
      <childName>A</childName>
      <childBez>ABEZ</childBez>
   </MyElement>
   <MyElement>
      <childName>B</childName>
      <childBez>BBEZ</childBez>
   </MyElement>
</TEST>


标签: xslt xpath