XML扁平编号索引转换为实际树结构 – XSLT

huangapple go评论54阅读模式
英文:

XML flat numbered index to real tree structure - XSLT

问题

XML树形编号索引到真实树结构 - XSLT

我正在尝试制作一个带有逻辑嵌套编号的索引树,每个部分在逻辑上嵌套在彼此内部。我正在使用XSLT

2.0,并一直尝试使用for-each-group ... group-starting-with,但效果甚微。

这是一个样本输入的XML:

<DOC>
  <section class="AB">
    <h1>Real section header</h1>
    <p><b>1. heading</b></p>
    <p>Some text here.</p>
    <p>More text.</p>
    <p><b>1.1. setting</b></p>
    <p>More words.</p>
    <p><b>1.2. fremmer</b></p>
    <p><b>1.2.1. point</b></p>
    <p>We are sailing.</p>
    <p>Whisky in the jar.</p>
    <p><b>1.2.2.</b></p>
    <p>Johnny is the man.</p>
    <p><b>1.2.3.</b></p>
    <p>And we go on and on.</p>
    <ul>
      <li>List item one</li>
      <li>List item two</li>
      <li>List item three</li>
    </ul>
    <p><b>2. Another heading</b></p>
    <p>Here is the accompanying text.</p>
    <table>
      <tr>
        <td>1</td>
        <td>Bla bla bla.</td>
      </tr>
      <tr>
        <td>2</td>
        <td>BlaX bla bla.</td>
      </tr>
      <tr>
        <td>3</td>
        <td>BlaY bla bla.</td>
      </tr>
    </table>
    <p><b>3. Last heading</b></p>
    <p>Here is the accompanying text right now.</p>
  </section>
</DOC>

这是输出应该是什么样子的:

<DOC>
  <section class="AB">
    <h1>Real section header</h1>
    <section>
      <h1>1. heading</h1>
      <p>Some text here.</p>
      <p>More text.</p>
      <section>
        <h1>1.1. setting</h1>
        <p>More words.</p>
      </section>
      <section>
        <h1>1.2. fremmer</h1>
        <section>
          <h1>1.2.1. point</h1>
          <p>We are sailling.</p>
          <p>Whisky in the jar.</p>
        </section>
        <section>
          <h1>1.2.2.</h1>
          <p>Johnny is the man.</p>
        </section>
        <section>
          <h1>1.2.3.</h1>
          <p>And we go on and on.</p>
          <ul>
            <li>List item one</li>
            <li>List item two</li>
            <li>List item three</li>
          </ul>
        </section>
      </section>
    </section>
    <section>
      <h1>2. Another heading</h1>
      <p>Here is the accompanying text.</p>
      <table>
        <tr>
          <td>1</td>
          <td>Bla bla bla.</td>
        </tr>
        <tr>
          <td>2</td>
          <td>BlaX bla bla.</td>
        </tr>
        <tr>
          <td>3</td>
          <td>BlaY bla bla.</td>
        </tr>
      </table>
    </section>
    <section>
      <h1>3. Last heading</h1>
      <p>Here is the accompanying text right now.</p>
    </section>
  </section>
</DOC>
英文:

XML flat numbered index to real tree structure - XSLT

I am trying to make an index tree with numbered section logically nested within each other. I am using XSLT

2.0 and have been trying to use for-each-group ... group-starting-with to little avail.

Here is a sample input XML:

<DOC>
  <section class="AB">
    <h1>Real section header</h1>
    <p><b>1. heading</b></p>
    <p>Some text here.</p>
    <p>More text.</p>
    <p><b>1.1. setting</b></p>
    <p>More words.</p>
    <p><b>1.2. fremmer</b></p>
    <p><b>1.2.1. point</b></p>
    <p>We are sailing.</p>
    <p>Whisky in the jar.</p>
    <p><b>1.2.2.</b></p>
    <p>Johnny is the man.</p>
    <p><b>1.2.3.</b></p>
    <p>And we go on and on.</p>
    <ul>
      <li>List item one</li>
      <li>List item two</li>
      <li>List item three</li>
    </ul>
    <p><b>2. Another heading</b></p>
    <p>Here is the accompanying text.</p>
    <table>
      <tr>
        <td>1</td>
        <td>Bla bla bla.</td>
      </tr>
      <tr>
        <td>2</td>
        <td>BlaX bla bla.</td>
      </tr>
      <tr>
        <td>3</td>
        <td>BlaY bla bla.</td>
      </tr>
    </table>
    <p><b>3. Last heading</b></p>
    <p>Here is the accompanying text right now.</p>
  </section>
</DOC>

And this is what the output should be:

<DOC>
  <section class="AB">
    <h1>Real section header</h1>
    <section>
      <h1>1. heading</h1>
      <p>Some text here.</p>
      <p>More text.</p>
      <section>
        <h1>1.1. setting</h1>
        <p>More words.</p>
      </section>
      <section>
        <h1>1.2. fremmer</h1>
        <section>
          <h1>1.2.1. underpunkt</h1>
          <p>We are sailling.</p>
          <p>Whisky in the jar.</p>
        </section>
        <section>
          <h1>1.2.2.</h1>
          <p>Johnny is the man.</p>
        </section>
        <section>
          <h1>1.2.3.</h1>
          <p>And we go on and on.</p>
          <ul>
            <li>List item one</li>
            <li>List item two</li>
            <li>List item three</li>
          </ul>
        </section>
      </section>
    </section>
    <section>
      <h1>2. Another heading</h1>
      <p>Here is the accompanying text.</p>
      <table>
        <tr>
          <td>1</td>
          <td>Bla bla bla.</td>
        </tr>
        <tr>
          <td>2</td>
          <td>BlaX bla bla.</td>
        </tr>
        <tr>
          <td>3</td>
          <td>BlaY bla bla.</td>
        </tr>
      </table>
    </section>
    <section>
      <h1>3. Last heading</h1>
      <p>Here is the accompanying text right now.</p>
    </section>
  </section>
</DOC>

答案1

得分: 0

最终看起来好像你的输入与我评论中的建议不完全符合,因为内部的 p/b 没有空格和标题编号后的文本,但你当然可以使用不同的测试,例如:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:function name="mf:group" as="node()*">
    <xsl:param name="nodes" as="node()*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$nodes" group-starting-with="p[b[string-length(translate(replace(., '^([0-9.]+)[^0-9.]*$', '$1'), '0123456789', '')) = $level]]">
      <xsl:choose>
        <xsl:when test="self::p[b[string-length(translate(replace(., '^([0-9.]+)[^0-9.]*$', '$1'), '0123456789', '')) = $level]]">
          <section>
            <xsl:apply-templates select="."/>
            <xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
          </section>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="section

"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:sequence select="mf:group(node(), 1)"/> </xsl:copy> </xsl:template> <xsl:template match="section/p[b]"> <h1> <xsl:apply-templates/> </h1> </xsl:template> <xsl:template match="section/p/b"> <xsl:apply-templates/> </xsl:template> <xsl:mode on-no-match="shallow-copy"/> <xsl:output method="xml" indent="yes" html-version="5"/> <xsl:strip-space elements="*"/> </xsl:stylesheet>

这是 XSLT 3,对于 XSLT 2.0,你需要将 xsl:mode 声明拆分为如下:

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

并且在使用 tail(current-group()) 时改为使用 subsequence(current-group(), 2)

英文:

In the end it looks as if your input doesn't quite meet the suggestion from my comment as the inner p/bs don't have a space and text after the heading number but you can of course use a different test e.g.

&lt;xsl:stylesheet xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;
	xmlns:xs=&quot;http://www.w3.org/2001/XMLSchema&quot;
	xmlns:mf=&quot;http://example.com/mf&quot;
	exclude-result-prefixes=&quot;#all&quot;
	version=&quot;3.0&quot;&gt;
  
  &lt;xsl:function name=&quot;mf:group&quot; as=&quot;node()*&quot;&gt;
    &lt;xsl:param name=&quot;nodes&quot; as=&quot;node()*&quot;/&gt;
    &lt;xsl:param name=&quot;level&quot; as=&quot;xs:integer&quot;/&gt;
    &lt;xsl:for-each-group select=&quot;$nodes&quot; group-starting-with=&quot;p[b[string-length(translate(replace(., &#39;^([0-9.]+)[^0-9.]*$&#39;, &#39;$1&#39;), &#39;0123456789&#39;, &#39;&#39;)) = $level]]&quot;&gt;
      &lt;xsl:choose&gt;
        &lt;xsl:when test=&quot;self::p[b[string-length(translate(replace(., &#39;^([0-9.]+)[^0-9.]*$&#39;, &#39;$1&#39;), &#39;0123456789&#39;, &#39;&#39;)) = $level]]&quot;&gt;
          &lt;section&gt;
            &lt;xsl:apply-templates select=&quot;.&quot;/&gt;
            &lt;xsl:sequence select=&quot;mf:group(tail(current-group()), $level + 1)&quot;/&gt;
          &lt;/section&gt;
        &lt;/xsl:when&gt;
        &lt;xsl:otherwise&gt;
          &lt;xsl:apply-templates select=&quot;current-group()&quot;/&gt;
        &lt;/xsl:otherwise&gt;
      &lt;/xsl:choose&gt;
    &lt;/xsl:for-each-group&gt;
  &lt;/xsl:function&gt;

  &lt;xsl:template match=&quot;section

&quot;&gt; &lt;xsl:copy&gt; &lt;xsl:apply-templates select=&quot;@*&quot;/&gt; &lt;xsl:sequence select=&quot;mf:group(node(), 1)&quot;/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;xsl:template match=&quot;section/p[b]&quot;&gt; &lt;h1&gt; &lt;xsl:apply-templates/&gt; &lt;/h1&gt; &lt;/xsl:template&gt; &lt;xsl:template match=&quot;section/p/b&quot;&gt; &lt;xsl:apply-templates/&gt; &lt;/xsl:template&gt; &lt;xsl:mode on-no-match=&quot;shallow-copy&quot;/&gt; &lt;xsl:output method=&quot;xml&quot; indent=&quot;yes&quot; html-version=&quot;5&quot;/&gt; &lt;xsl:strip-space elements=&quot;*&quot;/&gt; &lt;/xsl:stylesheet&gt;

That is XSLT 3, for XSLT 2.0 you would need to spell out the xsl:mode declaration as e.g.

&lt;xsl:template match=&quot;@* | node()&quot;&gt;
  &lt;xsl:copy&gt;
    &lt;xsl:apply-templates select=&quot;@* | node()&quot;/&gt;
  &lt;/xsl:copy&gt;
&lt;/xsl:template&gt;

and use subsequence(current-group(), 2) instead of tail(current-group()).

huangapple
  • 本文由 发表于 2023年4月20日 01:32:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76057333.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定