使用正则表达式查找混合XML内容

时间:2012-08-12 16:27:34

标签: xml xslt xslt-2.0

在运行我的XSLT 2.0样式表期间,我需要找到某些文本(例如,“故事3.1”,“故事8.19”,“故事21.76”)并用它做一些事情(例如,将其包装在超链接中) )。找到这些实例并用它们做我想做的事情是简单的任务。我遇到的问题是,有时我可能混合了需要包含在超链接中的内容(例如,“故事3.1< i> a< / i>”)。我无法弄清楚如何做到这一点。

以下是一些示例数据和我的模板:

<p>Jack goes up the hill (story 3.1<i>a</i>) to fetch a pail of water.</p>

<xsl:template match="text()">
<xsl:variable name="content" as="xs:string" select="."/>
<xsl:analyze-string select="$content" regex="Story [0-9]*\.[0-9]*" flags="i">
  <xsl:matching-substring>
    <xsl:variable name="figureToTargetId">
      <xsl:analyze-string select="." regex="[0-9]*\.[0-9]*">
        <xsl:matching-substring>
          <xsl:value-of select="concat('s',.)"/>
        </xsl:matching-substring>
      </xsl:analyze-string>
    </xsl:variable>
    <a href="#{$figureToTargetId}"><xsl:value-of select="."/></a>        
  </xsl:matching-substring>
  <xsl:non-matching-substring><xsl:value-of select="."/>
  </xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>

在上述情况下,我想要“故事3.1&lt; i&gt; a&lt; / i&gt;”被包裹在超链接中。

我知道要理想地解决这个问题,我必须匹配text()以外的东西。我不确定那是什么。

我一直在探索的一种方法是使用xsl:for-each循环遍历文本节点集,并测试下一个文本节点是否恰好是一个字母字符长。如果是,则将其包装在与前一个文本节点相同的超链接中。 (由于各种原因,我知道在与上述reg ex匹配的文本节点之后的任何一个字母长文本节点应该被超链接到同一个目标。)但我希望有一个更优雅的解决方案。

1 个答案:

答案 0 :(得分:1)

此转化

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


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

 <xsl:template match="p/text()[matches(., 'Story [0-9]+(\.[0-9]+)')]">
    <xsl:variable name="vCur" select="."/>
    <xsl:variable name="pContent" select="string(.)"/>
    <xsl:analyze-string select="$pContent" regex="Story [0-9]*\.[0-9]*" flags="i">
      <xsl:matching-substring>
        <xsl:variable name="figureToTargetId">
          <xsl:analyze-string select="." regex="[0-9]*\.[0-9]*">
            <xsl:matching-substring>
              <xsl:value-of select="concat('s',.)"/>
            </xsl:matching-substring>
          </xsl:analyze-string>
        </xsl:variable>
        <a href="#{$figureToTargetId}">
         <xsl:value-of select="."/>
         <xsl:if test="not(matches($vCur, 'Story [0-9]+(\.[0-9]+).+$'))">
          <xsl:sequence select="$vCur/following-sibling::*[1]"/>
         </xsl:if>
        </a>
      </xsl:matching-substring>
      <xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
    </xsl:analyze-string>
 </xsl:template>
 <xsl:template match=
  "p/*[preceding-sibling::node()[1]
         [self::text()
        and
          matches(., 'Story [0-9]+(\.[0-9]+)$')]
         ]"/>
</xsl:stylesheet>

应用于此文档(提供的扩展包含两个有趣的案例):

<t>
    <p>Little Red Riding Hood (Story 3.1) </p>
    <p>Jack goes up the hill (Story 3.1<i>a</i>) to fetch a pail of water.</p>
</t>

生成想要的正确结果

<t>
      <p>Little Red Riding Hood (<a href="#s3.1">Story 3.1</a>) </p>
      <p>Jack goes up the hill (<a href="#s3.1">Story 3.1<i>a</i>
      </a>) to fetch a pail of water.</p>
</t>

<强>解释

我们检查匹配的子字符串是否是当前文本节点的后缀 - 如果是,那么我们也复制下面的第一个兄弟元素。

<强>更新

在评论中,OP已设置了新的额外要求 - 同时将<i>更改为<em>

这只需稍微更新上述解决方案:

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


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

 <xsl:template match="p/text()[matches(., 'Story [0-9]+(\.[0-9]+)')]">
    <xsl:variable name="vCur" select="."/>
    <xsl:variable name="pContent" select="string(.)"/>
    <xsl:analyze-string select="$pContent" regex="Story [0-9]*\.[0-9]*" flags="i">
      <xsl:matching-substring>
        <xsl:variable name="figureToTargetId">
          <xsl:analyze-string select="." regex="[0-9]*\.[0-9]*">
            <xsl:matching-substring>
              <xsl:value-of select="concat('s',.)"/>
            </xsl:matching-substring>
          </xsl:analyze-string>
        </xsl:variable>
        <a href="#{$figureToTargetId}">
         <xsl:value-of select="."/>
         <xsl:if test="not(matches($vCur, 'Story [0-9]+(\.[0-9]+).+$'))">
          <xsl:apply-templates mode="match" select="$vCur/following-sibling::*[1]"/>
         </xsl:if>
        </a>
      </xsl:matching-substring>
      <xsl:non-matching-substring><xsl:value-of select="."/></xsl:non-matching-substring>
    </xsl:analyze-string>
 </xsl:template>
 <xsl:template match=
  "p/*[preceding-sibling::node()[1]
         [self::text()
        and
          matches(., 'Story [0-9]+(\.[0-9]+)$')]
         ]"/>
 <xsl:template mode="match" match=
  "p/i[preceding-sibling::node()[1]
         [self::text()
        and
          matches(., 'Story [0-9]+(\.[0-9]+)$')]
         ]">
  <em><xsl:apply-templates/></em>
 </xsl:template>

</xsl:stylesheet>