XSLT:使用内容表元数据的递归模板

问题描述 投票:0回答:2

小提琴在这里。我正在尝试从内容表元数据创建一个唯一标识符,以供将来在文本数据库中使用。我使用

<xsl:for-each..
编写了一个递归调用模板,但我没有得到预期的结果。

来源:

  <html lang="en">
     <head>
        <title>Title</title>
     </head>
     <body>
        <h1 data-toc="1" data-stub="Title1">Title 1—General</h1>
        <h2 data-toc="5" data-stub="Part1">PART 1—DEFINITIONS</h2>
        <h3 data-toc="9" data-stub="Sec1">§ 1  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec1.1">§ 1.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part2">PART 2—THE COMMITTEE</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2">§ 2  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2.1">§ 2.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part3">PART 3—THE COUNCIL</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3">§ 3  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3.1">§ 3.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
     </body>
  </html>

样式表:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
 
     <xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
     <xsl:strip-space elements="*"/>
     
     <xsl:template match="@* | node()">
         <xsl:copy>
             <xsl:apply-templates select="@* | node()"/>
         </xsl:copy>
     </xsl:template>
 
     <xsl:template match="*[@data-toc]">
         
         <xsl:variable name="build-callcode">
             <xsl:call-template name="build-call-code">
                 <xsl:with-param name="this-toc-level" select="@data-toc"/>
                 <xsl:with-param name="this-stub" select="@data-stub"/>
             </xsl:call-template>
         </xsl:variable>
 
         <xsl:copy>
             <xsl:apply-templates select="@*"/>
             <xsl:attribute name="data-callcode" select="$build-callcode"/>
             <xsl:apply-templates/>
         </xsl:copy>
 
     </xsl:template>
 
     <xsl:template name="build-call-code">
         <xsl:param name="this-toc-level"/>
         <xsl:param name="this-stub"/>
 
         <xsl:for-each select="preceding::*[@data-toc &lt; $this-toc-level][1]">
             <xsl:call-template name="build-call-code">
                 <xsl:with-param name="this-toc-level" select="@data-toc"/>
                 <xsl:with-param name="this-stub" select="concat(@data-stub, $this-stub)"/>
             </xsl:call-template>
             <xsl:value-of select="concat(@data-stub, $this-stub)"/>
         </xsl:for-each>
     </xsl:template>
 
 </xsl:stylesheet>

期望的结果:

    <html lang="en">
        <head>
            <title>Title</title>
        </head>
        <body>
            <h1 data-toc="1" data-stub="Title1" data-callcode="Title1">Title 1—General</h1>
            <h2 data-toc="5" data-stub="Part1" data-callcode="Title1Part1">PART 1—DEFINITIONS</h2>
            <h3 data-toc="9" data-stub="Sec1" data-callcode="Title1Part1Sec1">§ 1  Definitions.</h3>
            <p>foo</p>
            <p>foo</p>
            <h3 data-toc="9" data-stub="Sec1.1" data-callcode="Title1Part1Sec1.1">§ 1.1  Foo.</h3>
            <p>foo</p>
            <p>foo</p>
            <h2 data-toc="5" data-stub="Part2" data-callcode="Title1Part2">PART 2—THE COMMITTEE</h2>
            <p>foo</p>
            <p>foo</p>
            <h3 data-toc="9" data-stub="Sec2" data-callcode="Title1Part2Sec2">§ 2  Definitions.</h3>
            <p>foo</p>
            <p>foo</p>
            <h3 data-toc="9" data-stub="Sec2.1" data-callcode="Title1Part2Sec2.1">§ 2.1  Foo.</h3>
            <p>foo</p>
            <p>foo</p>
            <h2 data-toc="5" data-stub="Part3" data-callcode="Title1Part3">PART 3—THE COUNCIL</h2>
            <p>foo</p>
            <p>foo</p>
            <h3 data-toc="9" data-stub="Sec3" data-callcode="Title1Part3Sec3">§ 3  Definitions.</h3>
            <p>foo</p>
            <p>foo</p>
            <h3 data-toc="9" data-stub="Sec3.1" data-callcode="Title1Part3Sec3.1">§ 3.1  Foo.</h3>
            <p>foo</p>
            <p>foo</p>
        </body>
    </html>
xslt-2.0
2个回答
4
投票

试试这个:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
  
  <xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="*[@data-toc]">
    
    <xsl:variable name="build-callcode">
      <!-- No need to call the template with any params -->
      <xsl:call-template name="build-call-code"/>
    </xsl:variable>
    
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:attribute name="data-callcode" select="$build-callcode"/>
      <xsl:apply-templates/>
    </xsl:copy>
    
  </xsl:template>
  
  <xsl:template name="build-call-code">
    <xsl:variable name="this-toc-level" select="@data-toc"/>

    <!-- Since all nodes are siblings let's use preceding-sibling::* -->
    <xsl:for-each select="preceding-sibling::*[@data-toc &lt; $this-toc-level][1]">
      <!-- No need to call the template with any params -->
      <xsl:call-template name="build-call-code"/>
    </xsl:for-each>

    <!-- Put the current @data-stub AFTER for-each-->
    <xsl:value-of select="@data-stub"/>

  </xsl:template>
  
</xsl:stylesheet>

1
投票

使用 XSLT 3(无论如何,当前支持的版本,例如 Saxon 都是 XSLT 3 处理器),您可以使用

xsl:accumulator
s:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">
  
  <xsl:accumulator name="data-toc" as="xs:integer*" initial-value="()">
    <xsl:accumulator-rule 
      match="*[@data-toc]" 
      select="let $current-data-toc := xs:integer(@data-toc) return ($value[. lt $current-data-toc], $current-data-toc)"/>
  </xsl:accumulator>
  
  <xsl:accumulator name="data-call-code" as="xs:string*" initial-value="()">
    <xsl:accumulator-rule match="*[@data-toc]"
      select="let $count := count(accumulator-before('data-toc'))
              return $value[position() lt $count], @data-stub"/>
  </xsl:accumulator>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="data-toc data-call-code"/>

  <xsl:template match="*[@data-toc]">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:attribute name="data-callcode" select="accumulator-before('data-call-code') => string-join()"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>
© www.soinside.com 2019 - 2024. All rights reserved.