如何使文本节点的内部元素在元素之后包含文本

时间:2019-05-06 17:33:31

标签: xml xslt xslt-2.0

xml文档的作者并未将元素中的所有文本都包含在内,这些元素将被转换为超链接。我想处理或预处理xml以包括必要的文本。我发现这很难描述,但是一个简单的例子应该可以说明我的尝试。 我正在使用XSLT 2.0。我已经针对各种情况进行了正则表达式处理,但无法弄清楚。

我知道如何使用perl / python正则表达式执行此操作,但是我不知道如何使用XSLT进行处理。

这是作者的“非常”简化的xml,在其中他们从glink元素中省略了“(第3页)”。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <para>
        Go look at figure <glink refid=1>Figure 22</glink> (Sheet 3). Then go do something else.
    </para>
</root>

这是我希望将其转换为glink标记中现在的“(第3张)”的地方:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <para>
        Go look at figure <glink refid=1>Figure 22 (Sheet 3)</glink>. Then go do something else.
    </para>
</root>

应进行此转换的情况是,在紧随其后的glink元素(此正则表达式):

\s\(Sheet \d\)

我目前有2个XSLT。第一个预处理XML以转换许多其他情况(使用正则表达式/ xsl:analyze-string)。从预处理的xml转换为HTML的第二个XSLT。第二个XSLT具有处理glink元素并将其转换为超链接的模板,但是该超链接应包括Sheet信息。

我认为先对其进行预处理,而将第二个XSLT保留下来比较容易,但是我总是欣赏更好的方法。
谢谢您的宝贵时间。

3 个答案:

答案 0 :(得分:1)

现有答案具有正确的方法,但我会加强正则表达式模式和匹配模式:

  <xsl:param name="pattern" as="xs:string">\s\(Sheet \d\)</xsl:param>

  <xsl:variable name="pattern2" as="xs:string" select="'^' || $pattern"/>
  <xsl:variable name="pattern3" as="xs:string" select="'^(' || $pattern || ')(.*)'"/>

  <xsl:template match="glink[@refid][following-sibling::node()[1][self::text()[matches(., $pattern2)]]]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:value-of select=". || replace(following-sibling::node()[1], $pattern3, '$1', 's')"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="text()[preceding-sibling::node()[1][self::glink[@refid]]][matches(., $pattern2)]">
      <xsl:value-of select="replace(., $pattern3, '$2', 's')"/>
  </xsl:template>

https://xsltfiddle.liberty-development.net/bFN1y9z/1

否则,我认为匹配和替换发生的次数多于glink之后(直接?)之后的模式,如您在https://xsltfiddle.liberty-development.net/bFN1y9z/2中所见。

我发布的代码使用XPath 3.1的||字符串连接运算符,但是如果目标是XSLT 2处理器,那么当然可以用普通的concat函数调用来代替。

答案 1 :(得分:1)

为了减少使用正则表达式功能,我将使用以下方法:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="glink">
    <xsl:variable name="vAnalyzedString">
        <xsl:analyze-string 
            select="following-sibling::node()[1][self::text()]"
            regex="^\s*\(Sheet\s+\d+\)">
            <xsl:matching-substring>
                <match>
                    <xsl:value-of select="."/>
                </match>
            </xsl:matching-substring>
            <xsl:non-matching-substring>
                <no-match>
                    <xsl:value-of select="."/>
                </no-match>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:variable>
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:apply-templates 
        select="$vAnalyzedString/match/text()"/>
    </xsl:copy>
    <xsl:apply-templates 
        select="$vAnalyzedString/no-match/text()"/>
  </xsl:template>

  <xsl:template match="text()[preceding-sibling::node()[1][self::glink]]"/>
</xsl:stylesheet>

输出:

<root>
   <para>
        Go look at figure <glink refid="1">Figure 22 (Sheet 3)</glink>. Then go do something else.
    </para>
</root>

请注意:已处理所有glink,但这些文本节点都不是第一个兄弟姐妹。可以使用xsl:analize-string指令,但是您将需要声明一个带有部分结果的变量,然后对这些结果进行导航。另外,这种方法可以轻松地让您进一步处理那些(现在)文本节点,并且它仅一个正则表达式处理

答案 2 :(得分:0)

您可以将这两个模板与身份模板结合使用:

<xsl:template match="glink">
    <xsl:copy>
        <xsl:copy-of select="@*|text()" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="normalize-space(replace(following::text()[1],'\s(\(Sheet \d\)).*',' $1'))" />
    </xsl:copy>
</xsl:template> 

<xsl:template match="text()[preceding-sibling::glink]">
    <xsl:value-of select="normalize-space(replace(.,'\s\(Sheet \d\)(.*)',' $1'))" />
</xsl:template> 

第一个将(Sheet 3)字符串包含到glink中,第二个将(Sheet 3)从随后的text()节点中排除。

结果是:

<root>
    <para>
        Go look at figure <glink refid="1">Figure 22 (Sheet 3)</glink>. Then go do something else.</para>
</root>