开发者

Find absolute position of xml tag with XSLT

开发者 https://www.devze.com 2022-12-22 05:14 出处:网络
I have this XML document where I want to modify using XSLT to a different format. The problem I\'m currently facing is finding the absolute position of a tag relative to root and not to the parent.

I have this XML document where I want to modify using XSLT to a different format. The problem I'm currently facing is finding the absolute position of a tag relative to root and not to the parent.

For instance take the following example:

<book>
  <section>
     <chapter>
     </chapter>
  </section>
</book>
<book>
  <section>
     <chapter>
     </chapter>
  </section>
</book>    <book>
  <section>
     <chapter>
     </chapter>
  </section>
</book>    <book>
  <section>
     <chapter>
     </chapter>
  </section>
</book>

Desired output:

<book id=1>
  <section id=1>
     <chapter id=1>
     </chapter>
  </section>
</book>
<book id=2>
  <section id=2>
     <chapter id=2>
     </chapter>
  </section>
</book>    
<book id=3>
  <section id=3>
     <chapter id=3>
     </chapter>
  </section>
</book>    
<book id=4>
  <section id=4>
     <chapter id=4>
     </chapter>
  </section>
</book>

To get the id for the book tag can be easily achieved by using the position(), but once we go down to section and chapter things get trickier.

A solution for this problem would be creating a global variables that wou开发者_JAVA技巧ld work as counters for section and chapter, which would increment every time one of these tags are found in the document, but variables in XSLT behave like constants.

thanks in advance,

fbr


How about

<?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:template match="*|@*|text()">
    <xsl:copy>
        <xsl:apply-templates select="*|@*|text()" />
    </xsl:copy>
</xsl:template>

<xsl:template match="book|section|chapter">
    <xsl:copy>
           <xsl:attribute name="ix">
                <xsl:value-of select="1 + count(preceding::*[name() = name(current())])"/>
            </xsl:attribute> 
        <xsl:apply-templates select="*|@*|text()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

("ix" used instead of "id" as you really shouldn't have multiple elements with the same id in your XML)


xsl:number was built for this sort of scenario.

It makes it very easy to produce various formatted numbers and counts and is used often in XSL-FO for things such as a Table of Contents and labels for figures and tables (e.g. figure 3.a, section 1.1, etc.)

I adjusted the sample XML by adding a document element in order to make it well formed.

Using this stylesheet produces the desired output.

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

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

    <xsl:template match="*">
        <xsl:copy>
            <xsl:attribute name="id">   
                <xsl:number format="1 " level="single" count="book"/>
            </xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>


Must the IDs be integers? An easy way to generate unique IDs would be to create them be appending their parents to them:

<book id="1">
  <section id="1.1">
     <chapter id="1.1.1">
     </chapter>
  </section>
</book>

In that case you can use position() and recursion to generate the IDs easily.

0

精彩评论

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