How to get the Particular XML elements indexes wit

2019-02-26 00:25发布

问题:

I want to convert one xml to another xml by using XSLT2.0.While doing so, i want to find out some XML elements indexes with respect to my scenaio i explained here...

This is XML Document :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:body>
        <w:sdt>
            <w:sdtContent>
                <w:p>
                    <w:pPr>
                        <w:pStyle w:val="TOC"></w:pStyle>
                    </w:pPr>
                </w:p>
            </w:sdtContent>
        </w:sdt>

        <w:p>   <!-- index value 0 -->
        </w:p>

        <w:p>   <!-- index value 1 -->
        </w:p>

        <w:Bookmark></w:Bookmark> <!-- index value 2 -->
        <w:Bookmark></w:Bookmark> <!-- index value 3 -->    

        <w:pict></w:pict> <!-- index value 4 -->

        <w:p>     <!-- index value 5 -->
        </w:p>

        <w:Bookmark></w:Bookmark> <!-- index value 6 -->
        <w:Bookmark></w:Bookmark> <!-- index value 7 -->        
        <w:p>  <!-- index value 8 -->
        </w:p>      

    </w:body>
</w:document>

So, I want to find the <w:Bookmark> elements indexes.

  1. If my xml documents contains this elements then i want to create one element named 'Bookmark' and set the attribute 'index'.
  2. If my xml documents does not contains this elements don't do anything...

The Index Count starts from zero and i need to omit <w:sdt> elements from calculating index.Please See my comment on the xml document.

My Required output is :

  <Document>
    <Bookmark indexes="2,3,6,7">
    </Bookmark>
    </Document>

回答1:

Try this ...

<xsl:stylesheet 
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:user="http://http://stackoverflow.com/questions/11356668"
  exclude-result-prefixes="xs fn w user">

<xsl:output indent="yes"/>

<xsl:function name="user:bookmark-index" as="xs:string">
  <xsl:param name="bookmark-node" as="element()"/>
  <xsl:for-each select="$bookmark-node">
    <xsl:value-of select="count( preceding-sibling::*) -
                          count( preceding-sibling::w:sdt)" />
  </xsl:for-each>
</xsl:function>

<xsl:template match="/">
 <Document>
  <Bookmark indexes="{
    fn:string-join( for $i in w:document/w:body/w:Bookmark return user:bookmark-index( $i), ',')
    }" />
 </Document>
</xsl:template>

</xsl:stylesheet>

... or this equivalent without functions ...

<xsl:stylesheet 
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  exclude-result-prefixes="xs fn w">

<xsl:output indent="yes"/>

<xsl:template match="/">
 <Document>
  <Bookmark indexes="{
    fn:string-join( for $i in w:document/w:body/w:Bookmark return
            xs:string( count($i/preceding-sibling::*) -
                       count($i/preceding-sibling::w:sdt)),
                  ',')
    }" />
 </Document>
</xsl:template>

</xsl:stylesheet>


回答2:

This short and simple transformation uses <xsl:number>:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
 exclude-result-prefixes="w xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*[//w:Bookmark]">
     <Document>
       <xsl:variable name="vIndexes" as="xs:string+">
        <xsl:apply-templates/>
       </xsl:variable>
       <Bookmark indexes="{string-join($vIndexes, ',')}"/>
     </Document>
 </xsl:template>

 <xsl:template match="w:Bookmark">
  <xsl:variable name="vPos" as="xs:integer">
    <xsl:number count="/*/w:body/*[not(self::w:sdt)]" level="any"/>
  </xsl:variable>

  <xsl:sequence select="string($vPos -1)"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
        <w:body>
            <w:sdt>
                <w:sdtContent>
                    <w:p>
                        <w:pPr>
                            <w:pStyle w:val="TOC"></w:pStyle>
                        </w:pPr>
                    </w:p>
                </w:sdtContent>
            </w:sdt>

            <w:p>   <!-- index value 0 -->
            </w:p>

            <w:p>   <!-- index value 1 -->
            </w:p>

            <w:Bookmark></w:Bookmark> <!-- index value 2 -->
            <w:Bookmark></w:Bookmark> <!-- index value 3 -->

            <w:pict></w:pict> <!-- index value 4 -->

            <w:p>     <!-- index value 5 -->
            </w:p>

            <w:Bookmark></w:Bookmark> <!-- index value 6 -->
            <w:Bookmark></w:Bookmark> <!-- index value 7 -->
            <w:p>  <!-- index value 8 -->
            </w:p>

        </w:body>
</w:document>

the wanted, correct result is produced:

<Document>
   <Bookmark indexes="2,3,6,7"/>
</Document>