使用密钥:使用插入的<p>元素</p> </ul>整合多个<ul>

时间:2012-07-25 18:38:09

标签: xslt xpath

我有多个xml实例,类似于以下内容:

<refbody>
<ul>
    <li><uicontrol>FOO</uicontrol>BAR</li>
</ul>
<p>Values: 000 - 999</p>
<ul>
    <li><uicontrol>FOO</uicontrol>BAR</li>
    <li><uicontrol>FOO</uicontrol>BAR</li>
    <li><uicontrol>FOO</uicontrol>BAR</li>
    <li><uicontrol>FOO</uicontrol>BAR</li>
</ul>
<p>Values:</p>
<p>lorem ipsum</p>
<p>lorem ipsum</p>
<p>lorem ipsum</p>
</refbody>

我希望将所有<ul>元素合并为一个<dl>

每个<li>都应该创建一个子<dlentry>

每个<uicontrol>应该成为<dt>的{​​{1}}个孩子。

<dlentry>中的其余文字应放在<li>元素中,该元素也是<dd>的子元素。

我的样式表(下面提供)已经完成了很多。我遇到麻烦的地方是最后一项要求:

任何<dlentry>都需要放在与其<p>关联的<dlentry>内。

所以期望的结果如下所示:

preceding-sibling::li[1]

我认为我需要使用密钥来执行此操作,但我无法让它正常工作。如果有人能告诉我我做错了什么,我真的很感激。

这是我的样式表:

<refbody>
<dl>
    <dlentry>
        <dt>FOO</dt>
        <dd>BAR
            <p>Values: 000 - 999</p>
        </dd>
    </dlentry>
    <dlentry>
        <dt>FOO</dt>
        <dd>BAR</dd>
    </dlentry>
    <dlentry>
        <dt>FOO</dt>
        <dd>BAR</dd>
    </dlentry>
    <dlentry>
        <dt>FOO</dt>
        <dd>BAR</dd>
    </dlentry>
    <dlentry>
        <dt>FOO</dt>
        <dd>BAR
            <p>Values:</p>
            <p>lorem ipsum</p>
            <p>lorem ipsum</p>
            <p>lorem ipsum</p>
        </dd>
    </dlentry>
</dl>
</refbody>

2 个答案:

答案 0 :(得分:1)

以下样式表无需使用键即可生成所需的输出。

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

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

    <xsl:template match="refbody">
        <xsl:copy>
            <dl>
                <xsl:apply-templates select="ul" />
            </dl>
        </xsl:copy>        
    </xsl:template>

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

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

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

    <xsl:template match="li/text()">
        <dd>
            <xsl:copy/>
            <xsl:apply-templates 
                 select="parent::li
                                 [not(following-sibling::li)]
                                 /parent::ul
                                 /following-sibling::p
                                 [
                                  count(preceding-sibling::ul) 
                                  = count(current()/../../preceding-sibling::ul)
                                    +1
                                 ]"/>
        </dd>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

<强>予。这种转变(我使用密钥的解决方案):

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:key name="kFollowing"
      match="node()[self::p or self::text() and preceding-sibling::ul]"
      use="generate-id(preceding::li[1])"/>

     <xsl:template match="/*[ul]">
         <refbody>
             <dl>
               <xsl:apply-templates/>
             </dl>
         </refbody>
     </xsl:template>

     <xsl:template match="li">
         <dlentry>
           <xsl:apply-templates/>
         </dlentry>
     </xsl:template>

     <xsl:template match="li/text()">
         <dd>
           <xsl:value-of select="normalize-space()"/>
         </dd>
     </xsl:template>

     <xsl:template match="li[last()]/text()">
         <dd>
           <xsl:value-of select="."/>
           <xsl:copy-of select="key('kFollowing', generate-id(..))"/>
         </dd>
     </xsl:template>

     <xsl:template match="uicontrol">
         <dt>
           <xsl:apply-templates/>
         </dt>
     </xsl:template>
     <xsl:template match="p/text()"/>
</xsl:stylesheet>

应用于提供的XML文档

<refbody>
    <ul>
        <li>
            <uicontrol>FOO</uicontrol>BAR
        </li>
    </ul>
    <p>Values: 000 - 999</p>
    <ul>
        <li>
            <uicontrol>FOO</uicontrol>BAR
        </li>
        <li>
            <uicontrol>FOO</uicontrol>BAR
        </li>
        <li>
            <uicontrol>FOO</uicontrol>BAR
        </li>
        <li>
            <uicontrol>FOO</uicontrol>BAR
        </li>
    </ul>
    <p>Values:</p>
    <p>lorem ipsum</p>
    <p>lorem ipsum</p>
    <p>lorem ipsum</p>
</refbody>

生成想要的正确结果

<refbody>
   <dl>
      <dlentry>
         <dt>FOO</dt>
         <dd>BAR
        <p>Values: 000 - 999</p>
         </dd>
      </dlentry>
      <dlentry>
         <dt>FOO</dt>
         <dd>BAR</dd>
      </dlentry>
      <dlentry>
         <dt>FOO</dt>
         <dd>BAR</dd>
      </dlentry>
      <dlentry>
         <dt>FOO</dt>
         <dd>BAR</dd>
      </dlentry>
      <dlentry>
         <dt>FOO</dt>
         <dd>BAR
        <p>Values:</p>
            <p>lorem ipsum</p>
            <p>lorem ipsum</p>
            <p>lorem ipsum</p>
         </dd>
      </dlentry>
   </dl>
</refbody>

<强> II。纠正所有发现错误后的解决方案:

<xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:key name="kValues" match="p"
        use="generate-id(preceding::li[1])"/>

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

    <xsl:template match="ul[ancestor::refbody][1]">
        <dl>
            <xsl:for-each select="child::li|following::li">
                <xsl:variable name="vValues" select="key('kValues',generate-id())"/>
                <dlentry>
                    <dt>
                        <xsl:value-of select="child::uicontrol"/>
                    </dt>
                    <dd>
                         <xsl:value-of select="text()[normalize-space()]"/>
                         <xsl:copy-of select="$vValues" />
                    </dd>
                </dlentry>
            </xsl:for-each>
        </dl>
    </xsl:template>

    <xsl:template match="p|ul|text()"/>
</xsl:stylesheet>

如果将此更正的转换应用于同一个XML文档(上图),则会生成所需的正确结果


<强> III。分析我发现的错误:

  1. 这是一个需要好看的错误:
  2. ...

    <xsl:key name="kValues" match="p"             
             use="generate-id[preceding::li[1]]"/> 
    

    ...

    此处任何p元素都由其generate-id子项的字符串值编制索引,其前缀为li

    但是,XML文档中没有名为generate-id的元素。

    真正意图是使用generate-id()功能。函数调用由函数名称组成,后跟一系列实际参数值,用常规圆括号()括起来。用方括号替换圆括号从根本上改变了表达式的语义,将函数调用转换为(相对)位置步骤。

    0.2。 :此

            <xsl:variable name="vValues">                               
              <xsl:value-of select="key('kValues',generate-id())"/>                           
            </xsl:variable>
    

    不仅是一种(非常)不良做法,而且往往是导致错误,就像在当前案例中一样。您正在创建树(文档节点),而不是元素的节点集。树总是至少有一个节点(文档节点)及其布尔值,因此始终为真。

    永远不要使用上面的语法。

    正确的变量声明是

                <xsl:variable name="vValues" select="key('kValues',generate-id())"/>
    

    0.3。没有模板规则可以阻止身份模板复制任何不需要的ulptext()节点,这些规则会出现在原始转换的结果中。