2014-10-15 1 views
0

Я сделал ошибку в предыдущем сообщении.Копирование и добавление родительского узла с несколькими дочерними элементами

У меня есть данные 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> 

Насколько я могу сказать, это не способ поставить цикл внутри цикла до тех пор, пока новая глава.

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

Спасибо, у меня этот рабочий :). Не могли бы вы объяснить строки: nrd22

+0

Я добавил комментарии в свой ответ, и я надеюсь, что этого будет достаточно. Если этот ответ будет полезен, пометьте галочкой слева (чтобы он выглядел зеленым). Таким образом, другие пользователи могут знать, что это принятый ответ. –

0

@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. В качестве альтернативы мы могли бы использовать generate-id(current()) внутри условия [].

  2. Ось preceding-sibling возвращает результаты в обратном порядке документа, поэтому элемент [1] является непосредственно предшествующим.

  3. Вместо цикла по главам в шаблоне корня с помощью for-each, это использует шаблоны для books и chapter, что некоторые из них могут сказать немного более идиоматических XSLT. Корневой шаблон по умолчанию позаботится о том, чтобы вызвать шаблон books.

+0

** 1. ** Вы, очевидно, не тестировали это. В XPath нет элемента '[0]'. Нумерация узлов начинается с 1. - ** 2. ** «* Нам нужно запомнить текущую главу в переменной' this' ... * «Нет, мы этого не делаем. Это именно то, для чего предназначена функция current(). - ** 3. ** Выражение 'previous-sibling :: chapter [1] = $ this' does ** NOT ** означает, что предыдущая глава ** является тем же самым узлом, что и« текущая »глава ; это означает только то, что ** значения ** двух узлов одинаковы (что может быть совпадением). –

+0

Спасибо и извините. Исправлено и теперь протестировано. Дайте мне знать, если я все еще ничего не вижу. –

+0

трюк 'generate-id' намного круче. +1! –

0

Я считаю, что просто - и эффективный - способ сделать это, используя ключ (!):

<?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> 
Смежные вопросы