Позиционная группировка с использованием XSLT - PullRequest
3 голосов
/ 22 июля 2011

У меня есть xml, который содержит несколько документов:

<document>
    <line id="0">
        <field id="0"><![CDATA[H:doc1]]></field>
    </line>
    <line id="1">
        <field id="0"><![CDATA[L:1]]></field>
    </line>
    <line id="2">
        <field id="0"><![CDATA[L:2]]></field>
    </line>
    <line id="3">
        <field id="0"><![CDATA[L:3]]></field>
    </line>
    <line id="4">
        <field id="0"><![CDATA[H:doc2]]></field>
    </line>
    <line id="5">
        <field id="0"><![CDATA[L:1]]></field>
    </line> 
</document>

H = заголовок документа и L = позиция.в этом примере есть два H, что означает два документа с номерами doc1 и doc2.В doc1 есть три позиции, а в doc2 - одна позиция.

как преобразовать данные с помощью xslt версии 1, чтобы получить такой результат:

<documents>
    <document>
        <header>
            <number>doc1</number>
        </header>
        <line-item>
            <line-number>1</line-number>
            <line-number>2</line-number>
            <line-number>3</line-number>
        </line-item>
    </document>
    <document>
        <header>
            <number>doc2</number>
        </header>
        <line-item>
            <line-number>1</line-number>
        </line-item>
    </document>
</documents>

Ответы [ 2 ]

5 голосов
/ 22 июля 2011

Это преобразование XSLT 1.0 :

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

 <xsl:key name="kFollowing"
   match="line[starts-with(field,'L:')]"
   use="generate-id(preceding-sibling::line
                         [starts-with(field,'H:')]
                         [1]
                    )"/>

 <xsl:template match="/">
     <documents>
      <xsl:apply-templates/>
     </documents>
 </xsl:template>

 <xsl:template match="line[starts-with(field,'H:')]">
  <document>
   <header>
    <number><xsl:value-of select="substring-after(field,'H:')"/></number>
    <line-item>
     <xsl:apply-templates mode="inGroup" select=
        "key('kFollowing', generate-id())"/>
    </line-item>
   </header>
  </document>
 </xsl:template>

 <xsl:template match="line" mode="inGroup">
  <line-number>
   <xsl:value-of select="substring-after(field,'L:')"/>
  </line-number>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<document>
    <line id="0">
        <field id="0"><![CDATA[H:doc1]]></field>
    </line>
    <line id="1">
        <field id="0"><![CDATA[L:1]]></field>
    </line>
    <line id="2">
        <field id="0"><![CDATA[L:2]]></field>
    </line>
    <line id="3">
        <field id="0"><![CDATA[L:3]]></field>
    </line>
    <line id="4">
        <field id="0"><![CDATA[H:doc2]]></field>
    </line>
    <line id="5">
        <field id="0"><![CDATA[L:1]]></field>
    </line>
</document>

дает желаемый, правильный результат :

<documents>
   <document>
      <header>
         <number>doc1</number>
         <line-item>
            <line-number>1</line-number>
            <line-number>2</line-number>
            <line-number>3</line-number>
         </line-item>
      </header>
   </document>
   <document>
      <header>
         <number>doc2</number>
         <line-item>
            <line-number>1</line-number>
         </line-item>
      </header>
   </document>
</documents>

Объяснение : Использование клавиш для удобного указания и выбора полной группы смежных "строк" после "заголовка".

3 голосов
/ 22 июля 2011

Здесь также представлено решение XSLT 2.0 с использованием <xsl:for-each-group starting-with="...">

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
     <documents>
      <xsl:for-each-group select="*"
           group-starting-with="line[starts-with(field,'H:')]">
          <document>
           <header>
            <number><xsl:value-of select="substring-after(field,'H:')"/></number>
            <line-item>
             <xsl:apply-templates select="current-group()[position() >1]"/>
            </line-item>
           </header>
          </document>
      </xsl:for-each-group>
     </documents>
 </xsl:template>

 <xsl:template match="line">
  <line-number>
   <xsl:value-of select="substring-after(field,'L:')"/>
  </line-number>
 </xsl:template>
</xsl:stylesheet>

применительно к предоставленному документу XML :

<document>
    <line id="0">
        <field id="0"><![CDATA[H:doc1]]></field>
    </line>
    <line id="1">
        <field id="0"><![CDATA[L:1]]></field>
    </line>
    <line id="2">
        <field id="0"><![CDATA[L:2]]></field>
    </line>
    <line id="3">
        <field id="0"><![CDATA[L:3]]></field>
    </line>
    <line id="4">
        <field id="0"><![CDATA[H:doc2]]></field>
    </line>
    <line id="5">
        <field id="0"><![CDATA[L:1]]></field>
    </line>
</document>

желаемый, правильный результат получается :

<documents>
   <document>
      <header>
         <number>doc1</number>
         <line-item>
            <line-number>1</line-number>
            <line-number>2</line-number>
            <line-number>3</line-number>
         </line-item>
      </header>
   </document>
   <document>
      <header>
         <number>doc2</number>
         <line-item>
            <line-number>1</line-number>
         </line-item>
      </header>
   </document>
</documents>

Объяснение : <xsl:for-each-group>, его атрибут group-starting-with, функция current-group().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...