【问题标题】:XSLT: merging multiple sequences into a single sequenceXSLT:将多个序列合并为一个序列
【发布时间】:2018-07-05 23:23:46
【问题描述】:

假设我有一个这样的 XML 文档:

<?xml version="1.0" encoding="UTF-8"?>
<everything>
    <something>
        <part>
            <prop type="a">1</prop>
            <prop type="b">2</prop>
            <prop type="c">3</prop>
        </part>
        <part>
            <prop type="a">4</prop>
            <!--No b!-->
            <prop type="c">5</prop>
        </part>
    </something>
    <something>
        <part>
            <prop type="a">6</prop>
            <prop type="b">7</prop>
            <!--No c!-->
        </part>
    </something>
</everything>

我想要这样的输出:

<something>
   <props type="a">14</props>
   <props type="b">2?</props>
   <props type="c">35</props>
</something>

<something>
   <props type="a">6</props>
   <props type="b">7</props>
   <props type="c">?</props>
</something>

也就是说,首先我想知道文档中所有的prop/@types 是什么。在这种情况下,“a”、“b”和“c”。我已经找到了该部分的解决方案,我将对此问题进行掩饰。接下来,对于每个“某物”,我想遍历那些预期的道具元素,并将当前“某物”元素中该类型的所有道具合并到一个名为props 的新元素下。如果在 part 元素中找不到预期的 prop 元素之一,则插入问号。

这是我最好的尝试,如下。我认为它至少存在两个问题。一方面,在&lt;xsl:for-each select="$allTheProps/prop"&gt;̀ 循环的范围内,我不能像通常在$allTheProps 循环之外那样做select="part"。另一个问题是我认为你不能在路径模式中有一个变量,所以我无法测试@type=$nodeType。最终结果是我的 props 元素只是空的。我该如何解决这些问题?

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

    <xsl:variable name="allTheProps">
        <!--These elements are actually dynamically generated
            based on all prop types found in the document-->
        <prop type="a"></prop>
        <prop type="b"></prop>
        <prop type="c"></prop>
    </xsl:variable>

    <xsl:template match="something">
        <xsl:copy>
            <!--for each found type-->
            <xsl:for-each select="$allTheProps/prop">
                <xsl:variable name="nodeType" select="@type"/>

                <props>
                    <xsl:attribute name="type"><xsl:value-of select="$nodeType" />
                    </xsl:attribute>

                    <!--merge the props from each part of a something node-->
                    <xsl:for-each select="part">
                       <xsl:value-of select="prop[@type=$nodeType]"/>
                       <xsl:if test="not(prop[@type=$nodeType])">?</xsl:if>
                    </xsl:for-each> 
                </props>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:transform>

【问题讨论】:

  • 您还没有明确说明您需要 XSLT 1.0 还是 2.0 解决方案。请使用适当的标签。

标签: xml variables xpath foreach xslt-1.0


【解决方案1】:

在 XSLT 3.0 中你可以这样做

<xsl:template match="something">
  <something>
    <xsl:merge>
      <xsl:merge-source for-each="part" select="prop">
        <xsl:merge-key select="@type">
      </xsl:merge-source>
      <xsl:merge-action>
        <props type="{current-merge-key()}">
          <xsl:value-of select="string-join(current-merge-group(), '')"/>
        </props>
      </xsl:merge-action>
    </xsl:merge>
  </something>
</xsl:template>

【讨论】:

  • XSLT 3.0 听起来不错。不幸的是,我需要一个 1.0 解决方案。在某种程度上,1.0 的解决方案更容易,因为它不需要知道变量和 for-each 循环以外的任何内容。
  • 没关系。下次您需要 1.0 解决方案时,请说出来。
【解决方案2】:

您忘记了第一个 &lt;xsl:for-each select="$allTheProps/prop"&gt; 将上下文更改为变量 allTheProps,因此您的下一个 &lt;xsl:for-each select="part"&gt; 在此变量的上下文中执行,而 不是 在模板的上下文中执行你期待它(part)。因此,您最终会在 for-each 中得到一个空节点集。

一种解决方案是将上下文保存在一个变量中——在这里我将它命名为subTree——并访问这个变量。因此,只需两个小改动就能让您的代码按预期工作。

<xsl:template match="something">
    <xsl:variable name="subTree" select="*" />        <!-- save context -->

    <xsl:copy>
        <!--for each found type-->
        <xsl:for-each select="$allTheProps/prop">
            <xsl:variable name="nodeType" select="@type"/>

            <props>
                <xsl:attribute name="type"><xsl:value-of select="$nodeType" />
                </xsl:attribute>

                <!--merge the props from each part of a something node-->
                <xsl:for-each select="$subTree">     <!-- access variable -->
                   <xsl:value-of select="prop[@type=$nodeType]"/>
                   <xsl:if test="not(prop[@type=$nodeType])">?</xsl:if>
                </xsl:for-each> 
            </props>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

【讨论】:

  • 太棒了。我想我错了,你可以在 selecttest 模式中使用变量。
猜你喜欢
  • 2020-10-13
  • 2022-06-27
  • 1970-01-01
  • 2017-05-30
  • 2013-10-18
  • 2018-05-04
  • 1970-01-01
  • 2014-03-30
  • 2021-07-09
相关资源
最近更新 更多