【问题标题】:Adding a node's text string into it's previous sibling's child text string将节点的文本字符串添加到其上一个兄弟节点的子文本字符串中
【发布时间】:2012-09-19 20:08:29
【问题描述】:

我试图将一个节点移动到它的前一个兄弟姐妹的孩子中,而所有东西都在同一级别上的事实让我有点棘手。

我的输入说明:

<dl>
   <dlentry>
      <dt> Title 1 </dt>
      <dd> Title 1's definition </dd>
      <dt> Title 2 </dt>
      <dd> Title 2's definition </dd> 
      <dt> Title 3 </dt>
      <dd> Title 3's definition </dd>
   </dlentry>
</dl> 
<p> part of title 3's definition </p>
<p> another part of title 3's definition </p>

我想要做的是将底部的这 2 个 &lt;p&gt; 元素连接到 &lt;dd&gt; 元素文本的末尾 &lt;dlentry&gt; 因为它们是“标题”定义的一部分3"。

期望的输出:

<dl>
   <dlentry>
      <dt> Title 1 </dt>
      <dd> Title 1's definition </dd>
      <dt> Title 2 </dt>
      <dd> Title 2's definition </dd> 
      <dt> Title 3 </dt>
      <dd> Title 3's definition part of title 3's definition another part of title 3's  definition </dd>
   </dlentry>
</dl>

我正在处理的另一个问题是因为我的源文档中的 XHTML 有多糟糕,我需要对这些 &lt;p&gt; 元素的文本进行正则表达式匹配,以确保它不会命中其他任何地方在文档中。

我能够根据需要成功插入第一个 &lt;p&gt; 的文本,但无法让它工作,因此我可以进行正则表达式匹配并获得第二个

元素的文本也放入所需的位置。

这是我的样式表中使用 XSLT 2.0 的代码片段。

<xsl:analyze-string select="."
      regex="my regex expression here">

<xsl:template match="dlentry">

  <xsl:matching-substring>
    <dlentry>
     ** <xsl:copy-of select="node()[ position() lt last()]"/>
         <dd>
           <xsl:copy-of select="node()[last()]/text()" />
           <xsl:copy-of select=" parent::node()/following-sibling::node()[1]/text()"/>
         </dd>
    </dlentry>
  </xsl:matching-substring>

  <xsl:non-matching-substring>
      <xsl:value-of select=".">
  </xsl:non-matching-substring>

</xsl:template>

<xsl:template match="p[preceding-sibling::node()[1][self::node()[name(.)='dl']]]" />
<xsl:template match="p[preceding-sibling::node()[2][self::node()[name(.)='dl']]]" />

在带有 ** 星号的代码行中,Saxon 抛出一个错误,提示“Axis step child::node() cannont be used here: the context item is an atomic value。”我不熟悉分析字符串,但如果我在分析字符串之外运行我的选择副本并且只是在模板中,它运行良好。

很抱歉这个问题有点长,但我想分享到目前为止我所拥有的一切。

提前致谢。

【问题讨论】:

    标签: xml xslt xpath xslt-2.0


    【解决方案1】:

    这个简短的 XSLT 1.0(当然它也是 XSLT 2.0):

    <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="p" use="generate-id(preceding-sibling::dl[1])"/>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="dlentry/dd[last()]">
      <dd>
       <xsl:apply-templates select=
        "(.|key('kFollowing', generate-id(ancestor::dl[1])))/text()"/>
      </dd>
     </xsl:template>
     <xsl:template match="p"/>
    </xsl:stylesheet>
    

    应用于提供的 XML 文档时

    <html>
        <dl>
            <dlentry>
                <dt> Title 1 </dt>
                <dd> Title 1's definition </dd>
                <dt> Title 2 </dt>
                <dd> Title 2's definition </dd>
                <dt> Title 3 </dt>
                <dd> Title 3's definition </dd>
            </dlentry>
        </dl>
        <p> part of title 3's definition </p>
        <p> another part of title 3's definition </p>
    </html>
    

    产生想要的正确结果:

    <html>
       <dl>
          <dlentry>
             <dt> Title 1 </dt>
             <dd> Title 1's definition </dd>
             <dt> Title 2 </dt>
             <dd> Title 2's definition </dd>
             <dt> Title 3 </dt>
             <dd> Title 3's definition  part of title 3's definition  another part of title 3's definition </dd>
          </dlentry>
       </dl>
    </html>
    

    【讨论】:

    • 再次感谢 Dimitre!非常有效的方法。
    • @Laterade,也很方便:)
    • 与我的解决方案相比,实际上有两个不同之处。首先不同的是,该解决方案需要将整个 dom 树加载到内存中,以便创建所需的键索引。第二个区别在于

      元素的处理方式。此解决方案将包括 dl 之后的所有

      -元素。我的解决方案将只包括那些直接在

      元素之后的

      元素。您可以通过在

      元素之间放置

      标签来尝试输出差异。话虽如此,这仍然是一个不错的解决方案,值得 +1。
    • @SamiKorhonen,实际上,XSLT 要求将源 XML 文档的完整表示(DOM 或类似的东西)放在 RAM 中——这不仅限于使用键。至于第二点,OP 并没有给我们更具体的 XML 文档,我也不想徒劳地猜测。如果提供了更具体的文档,则很容易修改此解决方案以处理新的细节。感谢您的支持。
    • 虽然大多数实现确实将输入文档的完整表示加载到内存中,但符合标准的处理器可以以仅将文档的某些部分保留在内存中的方式编写。但我认为关于实现的讨论是题外话,所以我会留在这里。
    【解决方案2】:

    不确定效率,但遵循 xsl 应该会产生请求的输出:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="xml" indent="yes"/>
     <xsl:template match="/doc">
      <xsl:for-each select="dl">
       <dl>
        <xsl:for-each select="dlentry">
          <xsl:apply-templates select="dt|dd"/>
        </xsl:for-each>
       </dl>
      </xsl:for-each>
     </xsl:template>
    
     <xsl:template match="dt">
      <dt><xsl:value-of select="."/></dt>
     </xsl:template>
    
     <xsl:template match="dd">
      <dd>
        <xsl:value-of select="."/>
        <!-- Check if this is the last element (= no dd/dd after) -->
        <xsl:if test="not(following-sibling::*)">
         <!-- Select dl's next sibling, if it's a <p> -->
         <xsl:for-each select="../../following-sibling::*[1][name() = 'p']">
          <!-- Call recursive template -->
          <xsl:call-template name="concat"/>
         </xsl:for-each>
        </xsl:if>
      </dd>
     </xsl:template>
    
     <xsl:template name="concat">
       <xsl:value-of select="."/>
       <!-- Select p's next sibling, if it's a <p> -->
       <xsl:for-each select="following-sibling::*[1][name() = 'p']">
        <!-- Call recursive template -->
        <xsl:call-template name="concat"/>
       </xsl:for-each>
     </xsl:template>
    </xsl:stylesheet>
    

    这是我测试它的输入:

    <doc>
     <dl>
       <dlentry>
          <dt> Title 1 </dt>
          <dd> Title 1's definition </dd>
          <dt> Title 2 </dt>
          <dd> Title 2's definition </dd>
          <dt> Title 3 </dt>
          <dd> Title 3's definition </dd>
       </dlentry>
     </dl>
     <p> part of title 3's definition </p>
     <p> another part of title 3's definition </p>
    </doc>
    

    【讨论】:

    • 似乎无法在我这边工作,在您的 for-each 语句中,您似乎想要选择 dlentry 的下一个同级 p,但 dlentry 没有同级。 dlentry 和 p 处于不同的级别。 dl 和 p 处于同一水平,如果我的问题中的间距不够清楚,对不起。
    • 啊抱歉,我匆忙写了代码 cmets,看起来它们与功能不匹配。它应该说明“如果 dl 的下一个兄弟是 p 元素”。我使用xmlper.com 测试了 xsl。请注意,我必须添加一个名为 doc 的根节点。
    • 嗯,好的,我会尝试让它与我的实现一起工作。
    猜你喜欢
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多