user5182503
user5182503

Reputation:

XSLT 2.0 paramer list/map with for each

This is my xslt file:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xpath-default-namespace="http://xmlns.jcp.org/xml/ns/persistence">

    <xsl:param name="childPath"/>
    <xsl:param name="childNode"/>

    <xsl:output method="xml" indent="yes"/>


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

    <xsl:template match="*[local-name()=$childNode]">
        <xsl:apply-templates select="document($childPath)//class"/>
    </xsl:template>
</xsl:stylesheet>

The idea is that I have one parent (template) xml file and N child xml files. So, I take parent and call this xslt file for the first child xml file, for the second and so on. This xslt file I call using maven-xml-plugin and for every call I need to set transormationSet that is rather verbose.

So, I want to call this xslt file only once and pass all parameters as a list or a map. For example

$childNodes = nodeOne;nodeTwo;nodeThree
$childPaths = pathOne;pathTwo;pathTree

or like map:

$children = nodeOne:pathOne;nodeTwo:pathTwo

Could anyone say how to make my xslt file work with such list/map parameters?

Upvotes: 0

Views: 528

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167426

Using an XPath 3.1/XSLT 3.0 map, as supported in Saxon 9.8 and later or Saxon-JS 2 or Altova XML 2017 R3 and later you could use

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:param name="children" as="map(xs:string, xs:string)" select="map { 'nodeOne' : 'pathOne', 'nodeTwo' : 'pathTwo' }"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="*[local-name()=map:keys($children)]">
      <xsl:apply-templates select="document($children(local-name()))//class"/>
  </xsl:template>

</xsl:stylesheet>

As an alternative to stick with your string values containing semicolon separated lists of values:

  <xsl:param name="childNodes">nodeOne;nodeTwo;nodeThree</xsl:param>
  
  <xsl:param name="childPaths">pathOne;pathTwo;pathTree</xsl:param>

  <xsl:variable name="childNodesSeq" select="tokenize($childNodes, ';')"/>
  
  <xsl:variable name="childPathsSeq" select="tokenize($childPaths, ';')"/>
  
  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="*[local-name()=$childNodesSeq]">
      <xsl:apply-templates select="document($childPathsSeq[index-of($childNodesSeq, local-name(current()))])//class"/>
  </xsl:template>

Upvotes: 0

arlac77
arlac77

Reputation: 81

One way is to introduce a template mode and iterate over the name / path pairs

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xpath-default-namespace="http://xmlns.jcp.org/xml/ns/persistence">

    <xsl:param name="children" select="'name1:path1;name2:path2'"/>

    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="/">
        <xsl:variable name="here" select="."/>
        <xsl:for-each select="tokenize($children,';')">
            <xsl:apply-templates mode="child" select="$here">
                <xsl:with-param name="name" tunnel="true" select="tokenize(.,':')[1]"/>
                <xsl:with-param name="path" tunnel="true" select="tokenize(.,':')[2]"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="*" mode="child">
        <xsl:param name="name" tunnel="true"/>
        <xsl:param name="path" tunnel="true"/>

        <xsl:if test="local-name()=$name">
            <xsl:apply-templates select="document($path)//class"/>
        </xsl:if>

        <xsl:apply-templates mode="child"/>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 0

Related Questions