【问题标题】:XSL:Add sibling node while creating value thrugh recursionXSL:通过递归创建值的同时添加兄弟节点
【发布时间】:2018-09-11 08:54:36
【问题描述】:

输入:

<root>
  <name>a,b,c,d,e,f,g,h,i,j,k,</name>
  <value>1,,3,,5,,7,,,,11,<value>
</root>

预期输出

<root>
  <out>a:1|c:3|e:5|g:7|k:11</out>
  <e>5</e>
  <j/>
</root>

我能够通过递归得到预期的'out。 但是我需要分别打印两个值,例如“e”和“j”。 循环时,如果名称是 'e' 或 'j',则应创建这些元素。我无法做到。

递归代码

<xsl:template match="//root">
      <xsl:param name="columnName" select="a,b,c,d,e,f,g,h,i,j,k"></xsl:param>

        <root>
           <out>
            <xsl:call-template name="merge">
                <xsl:with-param name="name" select="normalize-space(name)" />
                 <xsl:with-param name="value" select="normalize-space(value)" />
            </xsl:call-template>                    
          </out>
       </root>
     </xsl:template>

    <xsl:template name="merge">
        <xsl:param name="name" />
        <xsl:param name="value" />
        <xsl:param name="separator" select="','" />  
           <xsl:variable name="currentValue"  select="substring-before($value, $separator)"/>
            <xsl:if test="$currentValue!=''">
                <xsl:value-of select="substring-before($name, $separator)" /><xsl:text>:</xsl:text>
                <xsl:value-of select="$currentValue" /><xsl:text>|</xsl:text>
            </xsl:if>
            <xsl:call-template name="merge">
                <xsl:with-param name="value" select="normalize-space(substring-after($value, $separator))" />
                 <xsl:with-param name="name" select="normalize-space(substring-after($name, $separator))" />
            </xsl:call-template>
    </xsl:template> 

如何在循环中添加两个元素以获取“退出”。

【问题讨论】:

  • 您已将问题标记为xslt-2.0?您使用 XSLT 2 处理器吗?使用tokenize,您不需要递归模板。
  • 部分支持。 Oracle 的 XML 开发工具包(12c 第 1 版)仅部分支持 XSLT 2.0。不支持 XPath 2.0 函数 fn:tokenize、fn:matches 和 fn:replace。
  • 其实我认为Oracle XDK 对XSLT 2.0 的支持很少。以后请说明您使用的是什么版本的 XSLT,否则很容易让人写答案浪费您和他们自己的时间。

标签: xml xslt xslt-1.0 xslt-2.0


【解决方案1】:

另一种方法是将“合并”模板更改为&lt;a&gt;1&lt;/a&gt; 形式的输出元素,您可以将其存储在变量中并进行操作以获得所需的输出。

试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="//root">
    <xsl:variable name="nodes">
      <xsl:call-template name="merge">
        <xsl:with-param name="name" select="normalize-space(name)" />
        <xsl:with-param name="value" select="normalize-space(value)" />
      </xsl:call-template>              
    </xsl:variable>
    <root>
      <out>
        <xsl:value-of select="$nodes/*[normalize-space()]/concat(name(), ':', .)" separator="|" />
        <!-- Alternate approach if above does not work
        <xsl:for-each select="$nodes/*[normalize-space()]">
          <xsl:if test="position() > 1">|</xsl:if>
          <xsl:value-of select="concat(name(), ':', .)" />
        </xsl:for-each>
        -->
      </out>
      <xsl:copy-of select="$nodes/e" />
      <xsl:copy-of select="$nodes/j" />
    </root>
  </xsl:template>

  <xsl:template name="merge">
    <xsl:param name="name" />
    <xsl:param name="value" />
    <xsl:param name="separator" select="','" />  
    <xsl:variable name="currentName" select="substring-before($name, $separator)"/>
    <xsl:if test="$currentName!=''">
      <xsl:element name="{$currentName}">
        <xsl:value-of select="substring-before($value, $separator)" />  
      </xsl:element>
      <xsl:call-template name="merge">
        <xsl:with-param name="value" select="normalize-space(substring-after($value, $separator))" />
        <xsl:with-param name="name" select="normalize-space(substring-after($name, $separator))" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template> 
</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/6qVRKwD/1查看它的实际应用

【讨论】:

    【解决方案2】:

    当您来自程序性思维时,很自然地会想到“当我循环遍历这些值时,我会计算出稍后需要的其他东西作为副作用”。通常对于函数式编程,最好换一种方式思考。在输出中独立计算你需要的每一件事,不要试图在一次输入中计算几件事。

    但是,您可以做的是将变量预先计算为输入数据的函数,如果它们将不止一次有用的话。所以在 XSLT 2.0 中你可以这样做

    <xsl:variable name="names" select="tokenize(name, ',')"/>
    <xsl:variable name="values" select="tokenize(value, ',')"/>
    

    然后你可以写一个函数

    <xsl:function name="f:value" as="xs:string">
      <xsl:param name="key" as="xs:string"/>
      <xsl:sequence select="$values[index-of($names, $key)]"/>
    </xsl:function>
    

    然后你可以这样做,例如:

    <e><xsl:value-of select="f:value('e')"/></e>
    <j><xsl:value-of select="f:value('j')"/></j>
    

    如果您可以使用 XSLT 3.0,那么解决所有这些问题的自然方法就是使用映射。像这样:

    <xsl:variable name="names" select="tokenize(name, ',')"/>
    <xsl:variable name="map" as="map(*)"
      select="map:merge(
               for-each-pair($names, 
                             tokenize(value, ','),
                             function($k, $v) {map{$k, $v}}))"/>
    </xsl:variable>
    <out>
      <xsl:value-of select="$names ! (. || ':' || $map(.))" separator="|"/>
    </out>
    <e>{$map?e}</e>
    <j>{$map?j}</j>
    

    【讨论】:

    • 感谢您的快速回复。Oracle 的 XML 开发人员工具包(12c 第 1 版)仅部分支持 XSLT 2.0。不支持 XPath 2.0 函数 fn:tokenize、fn:matches 和 fn:replace。
    【解决方案3】:

    如果您至少支持 XSLT 2,那么您可以使用 tokenize 并将您的两个值序列转换为一些 XML,然后您可以进一步处理:

    <xsl:transform 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all" 
      version="2.0">
    
      <xsl:output method="xml" indent="yes" />
    
      <xsl:template match="root">
          <xsl:copy>
              <xsl:variable name="pairs">
                  <xsl:variable name="values" select="tokenize(value, ',')"/>
                  <xsl:for-each select="tokenize(name, ',')[. castable as xs:QName]">
                    <xsl:element name="{.}">
                      <xsl:variable name="pos" select="position()"/>
                      <xsl:value-of select="$values[$pos]"/>
                    </xsl:element>
                  </xsl:for-each>
              </xsl:variable>
              <out>
                  <xsl:value-of select="$pairs/(* except (e, j))[normalize-space()]/concat(name(), ':', .)" separator="|"/>
              </out>
              <xsl:copy-of select="$pairs/(e, j)"/>
          </xsl:copy>
      </xsl:template>
    
    </xsl:transform>
    

    http://xsltransform.hikmatu.com/gWcDMez

    使用 XSLT 3,您甚至可以将从 tokenize 返回的字符串序列推送到模板以构造中间 XML(尽管我不得不承认我喜欢使用 Michael Kay 已经发布的地图的建议,而不仅仅是这个用途XML):

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        expand-text="yes"
        version="3.0">
    
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match=".[. instance of xs:string]">
          <xsl:param name="values"/>
          <xsl:element name="{.}">{ let $pos := position() return $values[$pos] }</xsl:element>
      </xsl:template>
    
      <xsl:template match="root">
          <xsl:copy>
              <xsl:variable name="pairs">
                  <xsl:apply-templates select="tokenize(name, ',')[. castable as xs:QName]">
                      <xsl:with-param name="values" select="tokenize(value, ',')"/>
                  </xsl:apply-templates>
              </xsl:variable>
              <out>
                  <xsl:value-of select="$pairs/(* except (e, j))[normalize-space()]!(name() || ':' || .)" separator="|"/>
              </out>
              <xsl:copy-of select="$pairs/(e, j)"/>
          </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    

    https://xsltfiddle.liberty-development.net/bdxtqz/1

    【讨论】:

    • 感谢您的快速回复。Oracle 的 XML 开发人员工具包(12c 第 1 版)仅部分支持 XSLT 2.0。不支持 XPath 2.0 函数 fn:tokenize、fn:matches 和 fn:replace
    • @FayizTK,恐怕我根本不知道 Oracle 的 XML Developer Kit 在 XSLT 和 XPath 2 方面支持什么,所以我无法就如何重写上述 XSLT 提出好的建议2 建议。如果您重写使用tokenize 来创建中间元素以使用递归模板,那么&lt;xsl:copy-of select="$pairs/(e, j)"/&gt; 希望在支持序列的情况下有效,否则您将需要使用&lt;xsl:copy-of select="$pairs/*[self::e|self::j]"/&gt;
    • 不知道他们是否支持value-of上的separator属性,如果不支持,使用for-each select="$pairs/(* except (e, j))[normalize-space()]"并输出`value-of select="concat(name(), ':', .)" 里面。
    猜你喜欢
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 2015-02-17
    • 1970-01-01
    • 2014-02-05
    相关资源
    最近更新 更多