Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml XSL:通过递归创建值时添加同级节点_Xml_Xslt_Xslt 1.0_Xslt 2.0 - Fatal编程技术网

Xml XSL:通过递归创建值时添加同级节点

Xml XSL:通过递归创建值时添加同级节点,xml,xslt,xslt-1.0,xslt-2.0,Xml,Xslt,Xslt 1.0,Xslt 2.0,输入: a、 b,c,d,e,f,g,h,i,j,k, 1,,3,,5,,7,,,,11, 预期产出 <root> <name>a,b,c,d,e,f,g,h,i,j,k,</name> <value>1,,3,,5,,7,,,,11,<value> </root> a:1 | c:3 | e:5 | g:7 | k:11 5. 我能够通过递归得到预期的结果。 但我需要分别打印两个值,如“e”和“j”。 循

输入:


a、 b,c,d,e,f,g,h,i,j,k,
1,,3,,5,,7,,,,11,
预期产出

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

a:1 | c:3 | e:5 | g:7 | k:11
5.
我能够通过递归得到预期的结果。 但我需要分别打印两个值,如“e”和“j”。 循环时,如果名称是“e”或“j”,则应创建这些元素。我无法做到这一点

递归码

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

:
|

如何在循环时添加两个元素以获得“输出”?

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

然而,若变量不止一次有用,那个么您可以做的是将变量作为输入数据的函数进行预计算。因此,在XSLT2.0中,您可以

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

然后可以编写一个函数

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

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

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

{$map?e}
{$map?j}

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

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

另一种方法是将“合并”模板更改为表单的输出元素

您将问题标记为?您是否使用XSLT2处理器?使用
tokenize
您不需要递归模板。它部分支持。Oracle的XML开发人员工具包(12c版本1)仅部分支持XSLT2.0。XPath2.0函数fn:tokenize、fn:matches和fn:replace不受支持。实际上,我认为Oracle XDK对XSLT2.0的支持很少。今后,请明确您使用的XSLT版本,否则编写答案的人很容易浪费您和他们自己的时间。感谢您的快速回复。Oracle的XML开发人员工具包(12c版本1)仅部分支持XSLT 2.0。XPath 2.0函数fn:tokenize、fn:matches和fn:replace不受支持。感谢您的快速回复。Oracle的XML开发人员工具包(12c版本1)仅部分支持XSLT 2.0。XPath 2.0函数fn:tokenize、fn:matches和fn:replace不可用supported@FayizTK,我担心我根本不知道Oracle的XML开发工具包在XSLT和XPath2方面支持什么,所以我无法就如何重写上面的XSLT2建议提出好的建议。如果重写使用
标记化
来创建中间元素以使用递归模板,那么如果支持序列,则
很有可能会起作用,否则需要使用
。不知道它们是否支持
值上的
分隔符
属性,如果不支持,对每个select=“$pairs/(*除了(e,j))[normalize-space()]”使用
,并在内部输出`value of select=“concat(name(),':,)”。
<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>
<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>
<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>