2016-03-17 2 views
0

Используя XSLT 1.0, мне нужно сгруппировать каждый узел между командами обработки <?start?> и <?end?>. Узлы вне этой пары на выходе должны быть неизменными.Группирование узлов XML между двумя командами обработки с использованием XSLT 1.0

Во-первых, мне нужно найти способ выбора только узлов, которые находятся между каждой стартовой парой. Предположим, у нас есть пример ввода XML:

<root> 
    abc 
    <?start?> 
    def<Highlighted bold="yes"> 
    <Highlighted italic="yes">ghi</Highlighted> 
    </Highlighted>jkl 
    <?pi?> 
    <table> 
     <Caption>stu</Caption> 
    </table>vw 
    <?end?> 
    xy 
    <?start?> 
    abc <Caption>def</Caption> ghi 
    <?end?> 
    jkl 
</root> 

Кроме того, мне нужно иметь узлы за пределами «старт - конец» пересечений на выходе, а также. Это означает, что на выходе: а) узлы на пересечении начального ПИ будут в элементе группы; б) любой узел вне пересечения будет напечатан без изменений. Обратите внимание, что входной документ также не может иметь пару команд обработки начального конца.

Например, от данного входа, выход должен быть следующим:

<root> 
    abc 
    <group> 
    def<Highlighted bold="yes"> 
    <Highlighted italic="yes">ghi</Highlighted> 
    </Highlighted>jkl 
    <?pi?> 
    <table> 
     <Caption>stu</Caption> 
    </table>vw 
    </group> 
    xy 
    <group> 
    abc <Caption>def</Caption> ghi 
    </group> 
    jkl 
<root> 

Часть этого вопроса уже отвечал Finding all XML nodes between each two processing instructions. Но я борюсь с печатью узлов за пределами элемента группы без дублирования любого узла.

ответ

2

Код:

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

    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()[1]"/> 
     </xsl:copy> 
     <xsl:apply-templates select="following-sibling::node()[1]"/> 
    </xsl:template> 

    <xsl:template match="processing-instruction('start')"> 
     <group> 
      <xsl:apply-templates select="following-sibling::node()[1]"/> 
     </group> 
     <xsl:apply-templates select="following-sibling::processing-instruction('end')[1]/following-sibling::node()[1]"/> 
    </xsl:template> 

    <xsl:template match="processing-instruction('end')"/> 

</xsl:stylesheet> 

Этот XSLT использует рекурсивный подход обработки первого дочернего узла и узел первой следующей-родственный().

Первый шаблон обрабатывает все узлы, кроме начального и конечного pis. Второй шаблон добавляет элемент group к элементам между началом pi и end pi. Третий шаблон помогает удалить конец pi.

+1

прохладный решение. Мне это нравится. –

0

Изменить шаблон для root в

<xsl:template match="root"> 
    <xsl:copy> 
    <xsl:apply-templates select="key('start', '') | processing-instruction('start') | node()[preceding-sibling::processing-instruction()[1][self::processing-instruction('end')]] | key('end', '')"/>   
    </xsl:copy> 
</xsl:template> 

поэтому весь код становится

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

    <xsl:output indent="yes"/> 

    <xsl:key name="start" match="root/node()[not(self::processing-instruction('start'))]" use="generate-id(preceding-sibling::processing-instruction('start')[1])"/> 
    <xsl:key name="end" match="root/node()[not(self::processing-instruction('end'))]" use="generate-id(following-sibling::processing-instruction('end')[1])"/> 

    <xsl:template match="root"> 
     <xsl:copy> 
     <xsl:apply-templates select="key('start', '') | processing-instruction('start') | node()[preceding-sibling::processing-instruction()[1][self::processing-instruction('end')]] | key('end', '')"/>   
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="processing-instruction('start')"> 
     <xsl:variable name="end" select="following-sibling::processing-instruction('end')[1]"/> 
     <xsl:variable name="following-start" select="key('start', generate-id())"/> 
     <xsl:variable name="preceding-end" select="key('end', generate-id($end))"/> 
     <xsl:variable name="intersect" select="$following-start[count(. | $preceding-end) = count($preceding-end)]"/> 
     <group> 
      <xsl:copy-of select="$intersect"/> 
     </group> 
    </xsl:template> 

</xsl:stylesheet> 

Не очень хорошо протестирована, но я надеюсь, что она охватывает все узлы перед тем, между ними и после пись.

+0

Это работает правильно, если структура входного документа более сложна, например. в корневом элементе в примере ввода есть элемент подкадра. –

0

Даже если мне нравится рекурсивное решение от @Lingamurthy CS
Ее немного другое решение на основе ключа.

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

    <xsl:key name="pi" match="root/node()" 
      use="generate-id(preceding-sibling::processing-instruction() 
        [self::processing-instruction('start') or self::processing-instruction('end') ][1])"/> 

    <xsl:template match="@* | node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="root"> 
     <xsl:copy> 
     <xsl:apply-templates select="processing-instruction('start') | 
            processing-instruction('end') | 
            key('pi', '')"/>   
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="processing-instruction('start')"> 
     <group> 
      <xsl:apply-templates 
      select="key('pi', generate-id()) 
       [not(self::processing-instruction('end'))]" /> 
     </group> 
    </xsl:template> 

    <xsl:template match="processing-instruction('end')"> 
     <xsl:apply-templates 
      select="key('pi', generate-id())[not(self::processing-instruction('start'))]" /> 
    </xsl:template> 

</xsl:stylesheet>