【问题标题】:Cannot process elements inside cdata无法处理 cdata 中的元素
【发布时间】:2014-12-08 21:50:19
【问题描述】:

我正在尝试编辑 CDATA 中的链接:

 <paragraph>
    <![CDATA[
        <strong><a href="http://example.com/2014/12/08/article-title">Article Title</a></strong>Article Excerpt.
    ]]>
 </paragraph>

目标是将段落更改为&lt;p&gt;,同时在链接内添加额外的标签。例如,所需的输出可以是:(不是所有的&lt;paragraph&gt; 都有链接,有些只包含文本)

<p>
    <strong><a href="http://example.com/2014/12/08/article-title?tacking_id=12345" style="font-size:1.1em; color:#067ab4; line-height:100%">Article Title</a></strong>Article Excerpt.
</p>

我尝试了以下代码,但没有成功。

<xsl:template match="paragraph">
    <xsl:copy-of select="@*"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[<p>]]></xsl:text>
    <xsl:value-of select="." disable-output-escaping="yes"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[</p>]]></xsl:text>
</xsl:template>

<xsl:template match="text()[contains(.,'&lt;a href=&#34;') and contains(.,'&#34;>')]">

    <xsl:variable name="link" select="substring-before(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/>

    <xsl:text disable-output-escaping="yes"><![CDATA[<a href="]]></xsl:text>
    <xsl:value-of disable-output-escaping="yes" select="$link"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[&tracking_id=12345" ]]></xsl:text>
    <xsl:value-of select="$link_style"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text>
    <xsl:apply-templates select="child::node()"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[</a>]]></xsl:text>

</xsl:template>

【问题讨论】:

  • CData 中没有元素 - 这就是它的用途!假设您将 SQL 中的一行读入一个 xml 文件,并且其中一个字段包含 HTML 及其所有 &wtf​​;实体、未终止的
    标记和其他无效 XML。您将它放在 CData 部分中以防止 XML 尝试解析它。

标签: xml xslt xslt-1.0


【解决方案1】:

就 XML 处理器而言,paragraph 节点中的 CDATA 不包含链接、标签或除单个文本节点之外的任何内容。它只是一个字符串,所以如果你真的想改变它,你必须求助于一些棘手的字符串操作。

您遇到的第一个问题是,在匹配“段落”的模板中,您没有执行任何xsl:apply-templates,因此您的第二个可以匹配paragraph 下的文本节点的模板永远不会被调用。

因此,您的第一个模板应如下所示

<xsl:template match="paragraph">
    <p>
        <xsl:apply-templates />
    </p>
</xsl:template>

现在,在匹配文本节点的模板中,这很糟糕,但您的主要问题是您使用&lt;xsl:apply-templates select="child::node()"/&gt;。但它是一个文本节点。单个文本节点。它没有可以匹配的子节点。

如果你真的,真的想让它以这种方式工作,模板应该是这样的

<xsl:template match="text()[contains(.,'&lt;a href=&#34;') and contains(.,'&#34;>')]">
    <xsl:variable name="firstbit" select="substring-before(., '&lt;a href=&#34;')"/>
    <xsl:variable name="link" select="substring-before(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/>
    <xsl:variable name="lastbit" select="substring-after(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/>

    <xsl:value-of disable-output-escaping="yes" select="$firstbit"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[<a href="]]></xsl:text>
    <xsl:value-of disable-output-escaping="yes" select="$link"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[?tracking_id=12345" ]]></xsl:text>
    <xsl:value-of select="$link_style"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text>
    <xsl:value-of disable-output-escaping="yes" select="$lastbit"/>
</xsl:template>

这个答案可能说明了为什么尝试操纵 CDATA 是一个坏主意。

另一种不那么令人不快的方法是进行两次 XSLT 转换。

第一个看起来像这样

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="paragraph">
        <p>
            <xsl:value-of disable-output-escaping="yes" select="." />
        </p>
    </xsl:template>
</xsl:stylesheet>

这将输出以下内容:

<p>
    <strong><a href="http://example.com/2014/12/08/article-title">Article Title</a></strong>Article Excerpt.
</p>

然后在此中对 a 标记进行所需的转换就变得微不足道了......

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

    <xsl:template match="a">
        <a href="{@href}?tracking_id=12345" style="color:#067ab4;">
            <xsl:apply-templates />
        </a>
    </xsl:template>
</xsl:stylesheet>

然后输出以下内容:

<p>
     <strong><a href="http://example.com/2014/12/08/article-title?tracking_id=12345" style="color:#067ab4;">Article Title</a></strong>Article Excerpt.
</p>

因此,如果您可以更改输入 XML 以消除 CDATA,那么它会变得容易得多.....

【讨论】:

  • 嗨蒂姆,非常感谢!我会尝试你的第一个解决方案。问题是我不控制提要 - 我无法删除 CDATA,所以不得不找到一些讨厌的方法来使用它:(
  • 刚刚将您的解决方案应用到我的脚本中,效果很好,非常感谢蒂姆!
猜你喜欢
  • 2011-05-30
  • 2015-01-29
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 1970-01-01
  • 2010-10-07
  • 2018-07-29
  • 1970-01-01
相关资源
最近更新 更多