【问题标题】:XSLT deepening content structureXSLT 深化内容结构
【发布时间】:2010-01-21 10:17:00
【问题描述】:

给定以下结构:

<TITEL1>...</TITEL1>  
<p>..</p>
<TITEL2>...</TITEL2>  
<TITEL3>...</TITEL3>
<TITEL3>...</TITEL3>  
<P>...<P>  

有没有办法解决这个问题:

<TITEL1>
    <TITEL>...</TITEL>  
    <p>...</p>
    <TITEL2>
        <TITEL>...</TITEL>  
        <TITEL3>
            <TITEL>...</TITEL>
            <P>...</P>
        </TITEL3>
        <TITEL3>
            <TITEL>...</TITEL>
            <P>...</P>
        </TITEL3>
    </TITEL2>
</TITEL1>

或者换句话说,有没有办法让更高级别的标题包含在较低级别的标题和它们后面的所有内容中,从而创建一个嵌套结构。每个 TITEL1,2 和 3 标签的内容应该进入一个新的&lt;TITEL&gt;-element

【问题讨论】:

  • 旁注:正确的拼写是“title”

标签: xslt structure iterative-deepening


【解决方案1】:

使用 XSLT 2.0(由 Saxon 9 或 AltovaXML 工具实现),您可以使用 xsl:for-each-group group-starting-with 和递归函数:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/2010/mf"
  exclude-result-prefixes="xsd mf">

  <xsl:output indent="yes"/>

  <xsl:function name="mf:nest" as="element()*">
    <xsl:param name="elements" as="element()*"/>
    <xsl:param name="level" as="xsd:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="*[starts-with(local-name(), concat('TITEL', $level))]">
      <xsl:choose>
        <xsl:when test="self::*[starts-with(local-name(), concat('TITEL', $level))]">
          <xsl:element name="TITEL{$level}">
            <xsl:apply-templates select="."/>
            <xsl:sequence select="mf:nest(current-group() except ., $level + 1)"/>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="ROOT">
    <xsl:sequence select="mf:nest(*, 1)"/>
  </xsl:template>

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

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]">
    <TITEL>
      <xsl:apply-templates select="@* | node()"/>
    </TITEL>
  </xsl:template>

</xsl:stylesheet>

使用该样式表输入

<ROOT>
<TITEL1>Titel 1, 1</TITEL1>  
<p>..</p>
<TITEL2>Titel 2, 1</TITEL2>  
<TITEL3>Titel 3, 1</TITEL3>
<TITEL3>Titel 3, 2</TITEL3>  
<P>...</P>
</ROOT>

转化为输出

<TITEL1>
   <TITEL>Titel 1, 1</TITEL>
   <p>..</p>
   <TITEL2>
      <TITEL>Titel 2, 1</TITEL>
      <TITEL3>
         <TITEL>Titel 3, 1</TITEL>
      </TITEL3>
      <TITEL3>
         <TITEL>Titel 3, 2</TITEL>
         <P>...</P>
      </TITEL3>
   </TITEL2>
</TITEL1>

【讨论】:

  • 嗨,马丁,这很好。对于我当前的项目,我使用了我自己提出的 PHP 解决方案。代码没有它的一半好,但它必须满足一些其他因素,这些因素在这里解释起来需要很长时间。我肯定会在不久的将来使用此代码。感谢您的帮助!
【解决方案2】:

没有一种特别优雅的方式来做你想做的事。这(可能)是可能的,但它会涉及一些非常丑陋(而且速度很慢)的 XPath 查询,使用 following-sibling axispreceding-sibling 轴上的过滤器匹配回当前节点。

如果有可能,我建议在 XSLT 之外创建层次结构(在 C#、Java 等中)

如果你选择走这条可怕的道路,你会想要做这样的事情(未经测试):

<xsl:template match="TITEL1">
  <TITEL1>
    <xsl:apply-templates 
      select="following-sibling::(p|TITEL2)[(preceding-sibling::TITEL1)[1]=.]" />
  </TITEL1>
</xsl:template>

<xsl:template match="TITEL2">
  <TITEL1>
    <xsl:apply-templates 
      select="following-sibling::(p|TITEL3)[(preceding-sibling::TITEL2)[1]=.]" />
  </TITEL1>
</xsl:template>

...

这只是一个例子,我已经看到了匹配的问题。如果真的可能的话,提出最终的 XPath 查询将非常复杂。

【讨论】:

  • 我对c#或java一无所知。也许我应该尝试在 PHP 中执行此操作。
  • 我在论坛的 php 端问过同样的问题。如果可以使用 XSLT 完成,我仍然会尝试您的解决方案并欢迎其他解决方案。谢谢你的建议。
  • 你能告诉我让我的声望点达到 15 的最快方法是什么,这样我就可以给答案投票了吗?
  • 我对你的问题投了赞成票,但这只会让你在 11 上。将我的答案标记为“答案”会给你额外的 2,但除非它确实有帮助,否则不要这样做.否则,请提出/回答问题。
【解决方案3】:

如果您不能使用 XSLT 2.0,这里有一个 XSLT 1.0 样式表,它应该产生与我之前发布的 XSLT 2.0 样式表相同的结果:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:output indent="yes"/>

  <xsl:template match="ROOT">
    <xsl:apply-templates select="*[1]" mode="nest">
      <xsl:with-param name="level" select="1"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]" mode="nest">
    <xsl:param name="level"/>
    <xsl:choose>
      <xsl:when test="$level = substring-after(local-name(), 'TITEL')">
        <xsl:element name="TITEL{$level}">
          <xsl:apply-templates select="."/>
          <xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
            <xsl:with-param name="level" select="$level"/>
          </xsl:apply-templates>
        </xsl:element>
        <xsl:apply-templates select="following-sibling::*[starts-with(local-name(), concat('TITEL', $level))][1]" mode="nest">
          <xsl:with-param name="level" select="$level"/>
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." mode="nest">
          <xsl:with-param name="level" select="$level + 1"/>
        </xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="*[not(starts-with(local-name(), 'TITEL'))]" mode="nest">
    <xsl:param name="level"/>
    <xsl:apply-templates select="."/>
    <xsl:apply-templates select="following-sibling::*[1][not(starts-with(local-name(), concat('TITEL', $level)))]" mode="nest">
      <xsl:with-param name="level" select="$level"/>
    </xsl:apply-templates>
  </xsl:template>

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

  <xsl:template match="*[starts-with(local-name(), 'TITEL')]">
    <TITEL>
      <xsl:apply-templates select="@* | node()"/>
    </TITEL>
  </xsl:template>

</xsl:stylesheet>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多