开发者

XSLT moving items inside other tag at the same level

I have this problem for xslt:

This is the input file:

  <root>
   <header/>
   <item/>
   … other n-1 <item/>
   <header/>
   <item/>
   … other m-1 <item/>
  </root>

so header and item are at the same level (/root). it must be converted in something like:

<root2>
  <header2&开发者_StackOverflow中文版gt;
     <item2/>
     …<item2/> // the first n-items up
  </header2>
  <header2>
     <item2/>
     …<item2/> // the last m-items up
  </header2>
</root2>

so basically the first n-item must be moved in the first header while the second group of items must be moved in the second header. Any idea how to get this?

Thanks

Randomize


Sample XML:

<root>
    <header/>
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <header/>
    <item>5</item>
    <item>6</item>
    <item>7</item>
</root>

XSLT using grouping:

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

    <xsl:key name="k" match="item" use="count(preceding-sibling::header)"/>

    <xsl:template match="/">
        <root2>
            <xsl:apply-templates select="root/item[generate-id(.) = generate-id(key('k', count(preceding-sibling::header)))]" mode="a"/>
        </root2>
    </xsl:template>

    <xsl:template match="item" mode="a">
        <header2>
            <xsl:apply-templates select="key('k', count(preceding-sibling::header))"/>
        </header2>
    </xsl:template>

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

</xsl:stylesheet>

Or simple particular XSLT:

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

    <xsl:template match="/">
        <root2>
            <header2>
                <xsl:apply-templates select="root/item[count(preceding-sibling::header) = 1]"/>
            </header2>
            <header2>
                <xsl:apply-templates select="root/item[count(preceding-sibling::header) = 2]"/>
            </header2>
        </root2>
    </xsl:template>

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

</xsl:stylesheet>

Both produce the same output:

<root2>
  <header2>
    <item>1</item>
    <item>2</item>
    <item>3</item>
  </header2>
  <header2>
    <item>5</item>
    <item>6</item>
    <item>7</item>
  </header2>
</root2>


This is a simpler more efficient XSLT 1.0 solution, than counting all preceding siblings:

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

 <xsl:key name="kFollowing" match="item"
  use="generate-id(preceding-sibling::header[1])"/>

 <xsl:template match="/*">
  <root>
   <xsl:apply-templates select="header"/>
  </root>
 </xsl:template>

 <xsl:template match="header">
  <heather2>
   <xsl:copy-of select="key('kFollowing', generate-id())"/>
  </heather2>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<root>
    <header/>
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <header/>
    <item>5</item>
    <item>6</item>
    <item>7</item>
</root>

the wanted, correct result is produced:

<root>
   <heather2>
      <item>1</item>
      <item>2</item>
      <item>3</item>
   </heather2>
   <heather2>
      <item>5</item>
      <item>6</item>
      <item>7</item>
   </heather2>
</root>

Explanation:

A key is defined in such a way that for a header any of the immediately-following item elements is matched by the generate-id() of this header.

II. XSLT 2.0 solution:

<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="/*">
  <root>
   <xsl:for-each-group select="item"
        group-adjacent=
         "generate-id(preceding-sibling::header[1])">
     <header2>
      <xsl:sequence select="current-group()"/>
     </header2>
   </xsl:for-each-group>
  </root>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the same correct result is produced:

<root>
   <header2>
      <item>1</item>
      <item>2</item>
      <item>3</item>
   </header2>
   <header2>
      <item>5</item>
      <item>6</item>
      <item>7</item>
   </header2>
</root>

Explanation: Use of xsl:for-each-group, group-adjacent, current-group()

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜