简体   繁体   English

调用带有标记化参数和父上下文的XSLT模板

[英]Call XSLT template with tokenized parameter and parent context

Can a call to an XSLT template be setup such that it is called with the parent of the current context? 是否可以设置对XSLT模板的调用,以使其与当前上下文的父级一起调用?

My XML looks like the following with Job nodes that have 1+ child location nodes: 我的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>

I would like the output to be a single row per Job per Location OR SubLocation: 我希望输出是每个位置或子位置的每个作业的一行:

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

The core logic of my XSLT looks like: 我的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>

The JobRecord template needs to be called per location OR Sub location (if applicable), even though the contents of the output is at the Job node level. 即使输出内容在Job节点级别,也需要按位置或子位置(如果适用)调用JobRecord模板。 How can the sub locations be broken up or iterated through without loosing context of the parent? 如何在不丢失父级上下文的情况下分解或迭代子位置?

A workaround would be to pass all the Job level information as parameters but I'm looking for a more natural XSLT approach. 一种解决方法是将所有Job级别信息作为参数传递,但是我正在寻找一种更自然的XSLT方法。

You can do that by storing the values in a variable, as: 您可以通过将值存储在变量中来做到这一点,如下所示:

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

and eventually passing them as a parameter in your named template. 并最终将它们作为参数传递给命名模板。 The whole stylesheet is below. 整个样式表如下。

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

A common idiom in XSLT/XPath, to process only the first of two possible elements is to construct a sequence and select the first item in that sequence, ie for your case to select ((SubLocations, LocationCode)[1]) , as that way you get the elements SubLocations if it exists or otherwise the elements LocationCode . XSLT / XPath中仅处理两个可能元素中的第一个的惯用法是构造一个序列并选择该序列中的第一个项目,即根据您的情况选择((SubLocations, LocationCode)[1]) ,因为您可以通过以下方式获取元素SubLocations如果存在),否则获取元素LocationCode Then you can tokenize and send the result on to another template, instead of using call-template I would simply suggest to push the element Job to another template matching it with a named mode: 然后,您可以标记化并将结果发送到另一个模板,而不是使用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>

That is an XSLT 3 example working with Saxon 9.8 all editions, online at http://xsltfiddle.liberty-development.net/bFukv8i , but of course it can be adapted to XSLT 2 if needed by changing the last template to 这是一个与Saxon 9.8所有版本一起使用的XSLT 3示例,可以从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>

and removing the expand-text attribute on xsl:stylesheet , http://xsltransform.hikmatu.com/eiQZDbi 并删除xsl:stylesheet上的expand-text属性, http xsl:stylesheet //xsltransform.hikmatu.com/eiQZDbi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM