XML中的CDATA和缺失值XSLT

时间:2016-04-08 15:58:08

标签: xml xslt xml-parsing xslt-1.0 cdata

我无法在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,因为它将来自外部供稿。

由于 干杯

3 个答案:

答案 0 :(得分:1)

如前所述,源文档的作者不希望您将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处理器执行。

答案 1 :(得分:0)

以下是如何使用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>

答案 2 :(得分:0)

CDATA表示&#34;字符数据&#34;。 CDATA标签意味着&#34;这里的东西可能看起来像标记,但不要被愚弄,我希望它被视为纯文本&#34;。所以有人在这里兴奋;他们错误地使用CDATA来封装(至少对你来说)是标记而不是文本的东西。我不知道为什么人们这样做,但你唯一的补救办法是在处理数据之前修复损坏。

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

(a)使用纯文本处理工具(sed,awk,Perl)在XML解析之前简单地从文件中删除开始和结束CDATA标记。当然,只有当您知道CDATA部分的内容实际上是格式良好的XML时,才能执行此操作。

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