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

时间:2018-02-06 22:10:24

标签: xml xslt xslt-2.0 tokenize

是否可以设置对XSLT模板的调用,以便使用当前上下文的父项调用它?

我的XML如下所示,Job节点有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方法。

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

XSLT / XPath中常见的习惯用法,只处理两个可能元素中的第一个,就是构造一个序列并选择该序列中的第一个项目,即你的情况选择((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>

并删除expand-texthttp://xsltransform.hikmatu.com/eiQZDbi

上的xsl:stylesheet属性