【问题标题】:Parallel iteration over XML nodes with XSLT使用 XSLT 对 XML 节点进行并行迭代
【发布时间】:2012-11-14 13:36:40
【问题描述】:

我有一个这样的 XML:

<xml>
    <node1>1.1</node1>
    <node1>1.2</node1>
    <node1>1.3</node1>

    <node2>2.1</node2>
    <node2>2.2</node2>
    <node2>2.3</node2>

    <node3>3.1</node3>
    <node3>3.2</node3>
    <node3>3.3</node3>
</xml>

我想得到以下输出:
线路:1.1 + 2.1 + 3.1
线路:1.2 + 2.2 + 3.2
线:1.3 + 2.3 + 3.3

有没有一种方法可以同时遍历这些节点并跟踪我在三个列表中的每一个中的当前位置,还是我必须将这些项目包装到一个更大的块中并遍历块?

我使用的是 XSL 1.0。

【问题讨论】:

  • 第三个元素块的类型应该是&lt;node3&gt;而不是&lt;node2&gt;吗?
  • @A巴赫你是对的。盲复制粘贴。

标签: xml xslt xslt-1.0


【解决方案1】:

就这么简单

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node1">
   <xsl:variable name="vPos" select="position()"/>
     <xsl:value-of select=". + ../node2[$vPos] + ../node3[$vPos]"/>
     <xsl:text>&#xA;</xsl:text>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时(基于提供的文档):

<xml>
    <node1>1</node1>
    <node1>2</node1>
    <node1>3</node1>

    <node2>4</node2>
    <node2>5</node2>
    <node2>6</node2>

    <node3>7</node3>
    <node3>8</node3>
    <node3>9</node3>
</xml>

产生想要的正确结果

12
15
18

请注意

  1. 在 XSLT 1.0 和 XSLT 2.0 中,可以使用 FXSL 模板/函数zip-with3()

  2. 在 XPath 3.0 (XSLT 3.0) 中会有一个标准函数 map-pairs(),但没有 map-tripples() 标准函数。可以使用此函数生成中间结果,然后再次使用它来生成最终结果。

  3. 1234563 /li>

【讨论】:

  • 请注意strip-space 对完成这项工作至关重要,否则position()s 将不会排列,因为node1 模板中的当前节点列表将包含(空格) node1 元素之间的文本节点。为了更加健壮,添加一个&lt;xsl:template match="xml"&gt;&lt;xsl:apply-templates select="node1" /&gt;&lt;/xsl:template&gt; 以使当前节点列表只包含三个node1 元素而不包含其他元素。
  • 我并不是要批评,我只是想强调这一点,作为一个学习点——根据我的经验,你对 XSLT 问题的回答总是正确的,但有时对于不那么熟悉的人来说相当神奇把 XSLT 当作你自己 ;-)
  • @IanRoberts,是的,你的观察是正确的——我希望我总是有时间进行详细的解释......
  • @DimitreNovaatchev,你已经帮助了我几次,现在又是一个很好的答案。只是稍微调整了一下以获得我需要的东西。谢谢!
【解决方案2】:

我认为 XSLT 中没有内置的方法可以做到这一点。我想您可以通过应用MapReduce 技术轻松地自己实现它。

【讨论】:

  • 抱歉,我不太明白如何使用它。你的意思是我应该首先映射&lt;node1/&gt; -&gt; &lt;node1&gt;&lt;value/&gt;&lt;id/&gt;&lt;node1&gt; 之类的列表,然后使用这些ID 来匹配列表位置?
  • 这跟MapReduce、并行执行等无关,他只是想按照文档顺序以外的顺序访问节点值。
【解决方案3】:

我认为以下内容可以完成工作(与xml 元素的子元素的数量和名称无关):

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text"/>

<xsl:key name="name" match="xml/*" use="local-name()"/>

<xsl:variable name="first" select="xml/*[generate-id() = generate-id(key('name', local-name())[1])]"/>

<xsl:template match="xml">
  <xsl:apply-templates select="$first[1]" mode="init"/>
</xsl:template>

<xsl:template match="xml/*" mode="init">
  <xsl:apply-templates select="key('name', local-name())" mode="line"/>
</xsl:template>

<xsl:template match="xml/*" mode="line">
  <xsl:variable name="pos" select="position()"/>
  <xsl:text>line: </xsl:text>
  <xsl:for-each select="$first">
    <xsl:if test="position() > 1"><xsl:text> + </xsl:text></xsl:if>
    <xsl:apply-templates select="key('name', local-name())[$pos]"/>
  </xsl:for-each>
  <xsl:text>&#10;</xsl:text>
</xsl:template>

<xsl:template match="xml/*">
  <xsl:value-of select="."/>
</xsl:template>

</xsl:stylesheet>

【讨论】:

  • Martin,您的问题似乎过于笼统了。我相信 OP 希望拥有相同位置元素的总和 - 每个元素都在单独的行上。此外,这样的概括太过分了——真正的 XML 文档可能包含其他不应该参与求和的兄弟。
  • 我看到您的回答在几分钟前被接受了,但我不清楚是否应该简单地计算三个不同但已知的子元素的总和,我认为应该输出元素的内容。
  • 是的,现在很明显 OP 想要计算总和。
猜你喜欢
  • 1970-01-01
  • 2012-08-07
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-16
相关资源
最近更新 更多