使用标记化参数和父上下文调用 XSLT 模板

Call XSLT template with tokenized parameter and parent context

能否设置对 XSLT 模板的调用,使其与当前上下文的父级一起调用?

我的 XML 与具有 1 个以上子位置节点的作业节点类似:

<Job>
  <JobId>12345</JobId>
  <JobTitle>Programmer</JobTitle>
    <Location>
      <LocationCode>US</LocationCode>
      <!-- there is a variable number of comma-deliminated strings within the sublocations node -->
      <SubLocations>US1,US2,US3</SubLocations>
    </Location>
    <Location>
      <LocationCode>CAN</LocationCode>
    </Location>
</Job>

我希望输出是每个作业每个位置或子位置的单行:

<Id>12345</Id><Title>Programmer</Title><Location>US1</Location>
<Id>12345</Id><Title>Programmer</Title><Location>US2</Location>
<Id>12345</Id><Title>Programmer</Title><Location>US3</Location>
<Id>12345</Id><Title>Programmer</Title><Location>CAN</Location>

我的 XSLT 的核心逻辑如下:

<xsl:template match="Job/Location">
  <xsl:choose>
<!-- Test for presence of sublocation -->
    <xsl:when test="SubLocation != null">
      <xsl:for-each select="distinct-values(SubLocation/tokenize(.,','))">
        <xsl:call-template name="JobRecord">
          <xsl:with-param name="Location">
            <xsl:value-of select="."/>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
<!-- No Sublocation present -->
      <xsl:call-template name="JobRecord">
        <xsl:with-param name="Location">
          <xsl:value-of select="/LocationCode"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="JobRecord">
  <xsl:param name="Location"/>
  <Id><xsl:value-of select="../JobId"/></Id>
  <Name><xsl:value-of select="../JobTitle"/></Name>
  <Location><xsl:value-of select="$Location"/></Location>
</xsl:template>

需要按位置或子位置(如果适用)调用 JobRecord 模板,即使输出内容处于作业节点级别。如何在不丢失父位置的情况下分解或迭代子位置?

解决方法是将所有作业级别信息作为参数传递,但我正在寻找更自然的 XSLT 方法。

您可以通过将值存储在变量中来实现,如:

<xsl:variable name="JID" select="preceding-sibling::JobId"/>
<xsl:variable name="JTITLE" select="preceding-sibling::JobTitle"/>

并最终将它们作为参数传递到您的命名模板中。整个样式表如下。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:strip-space elements="*"/>

    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="Job/Location">
        <xsl:variable name="JID" select="preceding-sibling::JobId"/>
        <xsl:variable name="JTITLE" select="preceding-sibling::JobTitle"/>
        <xsl:choose>
            <xsl:when test="SubLocations != ''">
                <xsl:for-each select="distinct-values(SubLocations/tokenize(.,','))">
                    <xsl:call-template name="JobRecord">
                        <xsl:with-param name="Location" select="."/>
                        <xsl:with-param name="JobID" select="$JID"/>
                        <xsl:with-param name="JobTITLE" select="$JTITLE"/>
                    </xsl:call-template>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="JobRecord">
                    <xsl:with-param name="Location" select="LocationCode"/>
                    <xsl:with-param name="JobID" select="$JID"/>
                    <xsl:with-param name="JobTITLE" select="$JTITLE"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="JobRecord">
        <xsl:param name="JobID"/>
        <xsl:param name="JobTITLE"/>
        <xsl:param name="Location"/>
        <Id><xsl:value-of select="$JobID"/></Id>
        <Name><xsl:value-of select="$JobTITLE"/></Name>
        <Location><xsl:value-of select="$Location"/></Location>
    </xsl:template>

    <xsl:template match="JobId|JobTitle"/>

</xsl:stylesheet>

XSLT/XPath 中的一个常见习语,只处理两个可能元素中的第一个是构建一个序列,select 该序列中的第一个项目,即对于你的情况 select ((SubLocations, LocationCode)[1]),这样你就可以得到元素 SubLocations(如果它存在),否则你可以得到元素 LocationCode。然后你可以标记并将结果发送到另一个模板,而不是使用 call-template 我只是建议将元素 Job 推到另一个与命名模式匹配的模板:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    expand-text="yes"
    version="3.0">

  <xsl:output omit-xml-declaration="yes"/>

  <xsl:template match="Job">
      <xsl:variable name="job" select="."/>
      <xsl:for-each select="Location/tokenize((SubLocations, LocationCode)[1], ',')">
          <xsl:apply-templates select="$job" mode="row">
              <xsl:with-param name="loc" select="current()"/>
          </xsl:apply-templates>
      </xsl:for-each>
  </xsl:template>

  <xsl:template match="Job" mode="row">
      <xsl:param name="loc"/>
      <Id>{JobId}</Id>
      <Title>{JobTitle}</Title>
      <Location>{$loc}</Location>
      <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

这是一个 XSLT 3 示例,适用于所有版本的 Saxon 9.8,在线 http://xsltfiddle.liberty-development.net/bFukv8i,但如果需要,当然可以通过将最后一个模板更改为

来适应 XSLT 2
  <xsl:template match="Job" mode="row">
      <xsl:param name="loc"/>
      <Id>
        <xsl:value-of select="JobId"/>
      </Id>
      <Title>
          <xsl:value-of select="JobTitle"/>
      </Title>
      <Location>
          <xsl:value-of select="$loc"/>
      </Location>
      <xsl:text>&#10;</xsl:text>
  </xsl:template>

并删除 xsl:stylesheethttp://xsltransform.hikmatu.com/eiQZDbi

上的 expand-text 属性