【问题标题】:Copying and adding a parent node with multiple children复制并添加具有多个子节点的父节点
【发布时间】:2014-10-15 08:30:57
【问题描述】:

我在之前的帖子中犯了一个错误。

我有这样工作的 XML 数据(这只是一个示例,章节和页面的数量都是可变的)。

<books>
 <chapter></chapter>
 <page></page>
 <page></page>
 <page></page>
 <chapter></chapter>
 <page></page>
 <page></page>
 <chapter></chapter>
 <page></page>
 <page></page>
 <page></page>
 <page></page>
</books>

我正在尝试重新创建它以使其看起来像这样

<books>
 <book>
  <chapter></chapter>
  <page></page>
  <page></page>
  <page></page>
 </book>
 <book>
  <chapter></chapter>
  <page></page>
  <page></page>
 </book>
 <book>
  <chapter></chapter>
  <page></page>
  <page></page>
  <page></page>
  <page></page>
 </book>
</books>

据我所知,在新章节出现之前,没有办法将循环放入循环中。

【问题讨论】:

  • 你确定它应该是这样工作的:每一章都有自己的书吗?

标签: xml xslt xpath xslt-1.0


【解决方案1】:

试试这样的:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <books>
            <xsl:for-each select="books/chapter">
                <!-- for each chapter node, record the number of preceding sibling,
                     for the first chapter there is none, so that is why I added +1,
                     so when I count all the preceding sibling chapter of page, I will
                     get a match -->
                <xsl:variable name="chapter_count" select="count(preceding-sibling::chapter) + 1"/>
                <book>
                    <xsl:copy-of select="."/>
                    <!-- This code will ensure that the following sibling pages that
                         will be copied has the same number of preceding sibling
                         chapter (for pages, notice that I did not add 1 in the
                         predicate). So for the first chapter node, $chapter_count is 1
                         and the number of preceding sibling chapters at page node is 1,
                         thus the match -->
                    <xsl:copy-of select="following-sibling::page[count(preceding-sibling::chapter) = $chapter_count]"/>
                </book>
            </xsl:for-each>
        </books>
    </xsl:template>

</xsl:stylesheet>

【讨论】:

  • 谢谢我有这个工作:)。你能解释一下:
  • 我在我的答案中添加了 cmets,我希望这就足够了。如果这个答案有帮助,请勾选左侧的复选标记(使其显示为绿色)。这样,其他用户就可以知道这是已接受的答案。
【解决方案2】:

我相信简单且高效(!)的方法是使用 key:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="page-by-chapter" match="page" use="generate-id(preceding-sibling::chapter[1])" />

<xsl:template match="/">
    <books>
        <xsl:for-each select="books/chapter">
            <book>
                <xsl:copy-of select=". | key('page-by-chapter', generate-id())"/>
            </book>
        </xsl:for-each>
    </books>
</xsl:template>

</xsl:stylesheet>

【讨论】:

    【解决方案3】:

    @JoelMLamsen 有正确的想法,他的解决方案可以正常工作,但可以稍微简化为不使用计数。我们将尝试直接表示的基本逻辑

    对于每一章,处理其前一章是这一章的后续页面。

    我们可以这样做:

    <xsl:template match="books">
        <books>
            <xsl:apply-templates select="chapter"/>
        </books>
    </xsl:template>
    
    <xsl:template match="chapter">
        <xsl:variable name="this" select="generate-id()"/>
        <book>
            <xsl:copy-of select="."/>
            <xsl:copy-of 
                select="following-sibling::page[generate-id(preceding-sibling::chapter[1]) = $this]"/>
        </book>
    </xsl:template>
    

    如果您需要帮助理解病情,您可以用英文阅读:

    following-sibling           of all the following
    ::page                      page elements
    [                           take the ones where
      generate-id(              the unique id of 
        preceding-sibling       of all its preceding
        ::chapter               chapter elements
        [1]                     (the most recent one)
      )
      =                         is equal to
      $this                     the unique id of the chapter we are on
    ]
    

    给刚接触 XSLT 的人的一些注意事项:

    1. 我们在this 变量中记住当前章节的唯一ID。或者,我们可以在[] 条件中使用generate-id(current())。

    2. preceding-sibling 轴以相反的文档顺序返回结果,因此[1] 元素是紧接在前面的元素。

    3. 不是使用for-each 循环根模板中的章节,而是使用books 和chapter 的模板,有些人可能会说这更符合XSLT 的习惯。默认根模板将负责调用books 模板。

    【讨论】:

    • 1. 你显然没有测试过这个。在 XPath 中,没有 [0] 元素。节点编号从 1 开始。 -- 2. "我们需要记住 this 变量中的当前章节..." 不,我们不需要。这正是 current() 函数的用途。 -- 3. 表达式preceding-sibling::chapter[1] = $this 确实NOT 表示前一章与“当前”章的节点相同;这仅表示两个节点的 值 相同(可能是巧合)。
    • 谢谢和抱歉。已修复,现已测试。如果我还有什么遗漏,请告诉我。
    猜你喜欢
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多