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, '<b>Country:</b>'), '<b>')" />
<p>
<xsl:text>Country: </xsl:text>
<xsl:value-of select="substring-before(substring-after($country-anchor, '>'), '<')"/>
</p>
</xsl:for-each>
</xsl:template>
一个更好的选择——如果您的处理链允许的话——分两步进行转换:首先,禁用 description
上的输出转义并将结果保存到文件中;然后将生成的文件处理为 XML.
这两个都可以使用 XSLT 1.0 处理器执行。
我无法在 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, '<b>Country:</b>'), '<b>')" />
<p>
<xsl:text>Country: </xsl:text>
<xsl:value-of select="substring-before(substring-after($country-anchor, '>'), '<')"/>
</p>
</xsl:for-each>
</xsl:template>
一个更好的选择——如果您的处理链允许的话——分两步进行转换:首先,禁用 description
上的输出转义并将结果保存到文件中;然后将生成的文件处理为 XML.
这两个都可以使用 XSLT 1.0 处理器执行。