XML 中的 CDATA 和缺失值 XSLT

CDATA in XML and missing values XSLT

我无法在 XSLT 转换中显示国家/地区值,也不确定如何处理 XSLT 中的 CDATA 标记

这是我的 XML:

<catalog xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
<cd>
    <title>Empire Burlesque</title>
    <description><![CDATA[
      <div>
        <b>Country:</b>
        <a href="location.html">Canada</a>
        <b>City:</b>
        <a href="location.html">Calgary</a>
      </div>            
    ]]></description>
</cd>
<cd>
    <title>Hide your heart</title>
    <description><![CDATA[
      <div>
        <b>Country:</b>
        <a href="location.html">Canada</a>
        <b>City:</b>
        <a href="location.html">Toronto</a>
      </div>    
    ]]></description>
</cd>
</catalog>

这是我的 XSLT:

<xsl:template match="/">
    <xsl:for-each select="catalog/cd">
        <p>Title: <xsl:value-of select="title"/></p> 
        <p>Country: <xsl:value-of select="description/div/b['Country:']/following-sibling::a" disable-output-escaping="yes"/></p>
   </xsl:for-each>
</xsl:template>

我的结果是:

<p>Title: Empire Burlesque</p>
<p>Country: </p> 
<p>Title: Hide your heart</p>
<p>Country: </p> 

如何显示我的国家/地区值。如果我从我的 XML 中删除 CDATA 标签,它就会起作用。但是,我无法修改 XML,因为它将来自外部提要。

谢谢 干杯

以下是如何使用 XSLT 2.0 以及 Saxon 9 的商业版本和 TagSoup HTML 解析器库 http://home.ccil.org/~cowan/XML/tagsoup/:

的帮助以干净的方式完成它
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    exclude-result-prefixes="xs saxon xhtml"
    version="2.0">

    <xsl:template match="/">
        <xsl:for-each select="catalog/cd">
            <p>Title: <xsl:value-of select="title"/></p> 
            <p>Country: <xsl:value-of select="saxon:parse-html(description)//xhtml:div/xhtml:b[. = 'Country:']/following-sibling::xhtml:a[1]"/></p>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

作为替代方案,对于任何 XSLT 2.0 处理器,您都可以使用由 David Carlisle 在 XSLT 2.0 本身中实现的 HTML 解析器:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns:d="data:,dpc"
    exclude-result-prefixes="xs saxon xhtml d"
    version="2.0">

    <xsl:import href="https://raw.githubusercontent.com/davidcarlisle/web-xslt/master/htmlparse/htmlparse.xsl"/>

    <xsl:template match="/">
        <xsl:for-each select="catalog/cd">
            <p>Title: <xsl:value-of select="title"/></p> 
            <p>Country: <xsl:value-of select="d:htmlparse(description)//xhtml:div/xhtml:b[. = 'Country:']/following-sibling::xhtml:a[1]"/></p>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

CDATA 表示 "character data"。 CDATA 标签表示 "the stuff in here might look like markup, but don't be fooled, I want it treated as plain text"。所以有人在这里嘘声;他们错误地使用 CDATA 来包含(至少对您来说)是标记而不是文本的东西。我不知道人们为什么这样做,但你唯一的补救办法是在处理数据之前修复损坏。

执行此操作的两种方法是:

(a) 在 XML 解析之前,使用纯文本处理工具(sed、awk、Perl)简单地从文件中删除开始和结束 CDATA 标记。当然,只有当您知道 CDATA 部分的内容实际上格式正确时,您才能这样做 XML.

(b) 处理提供的 XML 文档。 CDATA 部分将作为单个文本节点出现在您的 XSLT 代码中。要将其转换为节点树,您需要将其解析为 XML(CDATA 标记阻止封闭标记在第一次被识别为标记)。例如,您可以使用 XSLT 3.0 parse-xml() 或 parse-xml-fragment() 函数,或者通过调用扩展函数来执行此操作。同样,这依赖于知道内容格式正确 XML。如果它是 HTML 而不是 XML,有时是这种情况,您可以调用 HTML 解析器而不是 XML 解析器。

如前所述,源文档的作者不希望您将 description 元素的内容 解析为 XML - 否则他们不会将其标记为 CDATA 部分。

但是,您仍然可以将内容 解析为文本 - 尽管它比替代方法更难且更不可靠:

<xsl:template match="/">
    <xsl:for-each select="catalog/cd">
        <p>
            <xsl:text>Title: </xsl:text>
            <xsl:value-of select="title"/>
        </p> 
        <xsl:variable name="country-anchor" select="substring-before(substring-after(description, '&lt;b&gt;Country:&lt;/b&gt;'), '&lt;b&gt;')" />
        <p>
            <xsl:text>Country: </xsl:text>
            <xsl:value-of select="substring-before(substring-after($country-anchor, '&gt;'), '&lt;')"/>
        </p>
   </xsl:for-each>
</xsl:template>

一个更好的选择——如果您的处理链允许的话——分两步进行转换:首先,禁用 description 上的输出转义并将结果保存到文件中;然后将生成的文件处理为 XML.

这两个都可以使用 XSLT 1.0 处理器执行。