我正在努力将xml文件从旧版本转换为新版本。这是我正在使用的基本模板:
<xsl:template match="*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="@*"></xsl:copy-of>
<xsl:apply-templates></xsl:apply-templates>
</xsl:element>
</xsl:template>
但是,新版本的xml架构要求所有具有文本值的元素不应为空字符串。所以旧的xml文档如:
<dataset>
<title> </title>
</dataset>
在新版本中无效。我试图修改文本节点的默认模板。新的文本模板将检查文本节点,如果文本代码为空字符串,它将终止转换,否则它将值复制到输出xml。这是模板:
<xsl:template match="text()">
<xsl:variable name="text-value" select="."/>
<xsl:if test="normalize-space($text-value) = ''">
<xsl:message terminate="yes">
<xsl:call-template name="output_message3_fail">
<xsl:with-param name="parent_node" select="name(parent::node())"/>
</xsl:call-template>
</xsl:message>
</xsl:if>
<xsl:value-of select="$text-value"/>
</xsl:template>
然而,我发现输入是否如下:
<dataset>
<title>My tile</title>
</dataset
将调用新的文本模板。如果输入如下:
<dataset>
<title> </title>
</dataset>
永远不会调用新的文本模板,输出看起来像
<dataset>
<title/>
</dataset>
所以我的方法 - 修改文本模板,不起作用。你有什么建议怎么做 - 如果找到一个带空字符串的元素,就终止转换。
非常感谢!
顺便说一句,我使用的是java xalan xslt处理器。
答案 0 :(得分:2)
然而,我发现输入看起来 像:
<dataset> <title>My tile</title> </dataset>
将调用新的文本模板
是的,这正是提供的代码应该做的事情 - 我稍后会解释一下。
如果输入如下:
<dataset> <title> </title> </dataset>
新文本模板永远不会 调用和输出看起来像
<dataset> <title/> </dataset>
我无法使用Xalan(J或c)和许多其他XSLT处理器重现这一点(Saxon 6.5.3,.NET XslCompiledTransform和XslTransform,Msxml3,4,6,JD, ......等等。 所有这些都显示错误消息(在<xsl:message terminate="yes">
内)
产生上述输出的唯一XSLT处理器是AltovaXML(XmlSPY)。
如果您使用的是XmlSPY,可能您可以考虑尝试使用其他XSLT处理器或联系Altova寻求帮助。
现在,回到第一个行为。
<强>解释强>:
提供的源XML文件:
<dataset>
<title>My tile</title>
</dataset>
有三个文字节点:
第一个文本节点是<dataset>
和<title>
之间的节点,它只包含空格。
第二个文本节点是<title>
的唯一子节点,其值为字符串“My tile”。
第三个和最后一个文本节点位于</title>
和</dataset>
之间,仅包含空格。
当选择匹配text()
的模板来处理上述三个文本节点中的第一个时,测试结果是肯定的并且<xsl:message terminate="yes">
被执行 - 这正是报告的行为。
<强>解决方案强>:
存在一个简单的解决方案。 只需更改匹配text()
的模板,以仅匹配作为其父级的唯一文本节点的此类文本节点。现在,XSLT转换的行为符合最初提供的两种类型的XML文档的预期:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match=
"*[not(node()[2])]/text()
[normalize-space()='']">
<xsl:message terminate="yes">
<xsl:call-template name="output_message3_fail">
<xsl:with-param name="parent_node" select="name(..)"/>
</xsl:call-template>
</xsl:message>
</xsl:template>
<xsl:template name="output_message3_fail">
<xsl:param name="parent_node"/>
<xsl:message> ERROR:
<<xsl:copy-of select="$parent_node"/>> is empty
</xsl:message>
</xsl:template>
</xsl:stylesheet>
在提供的XML文档上应用此转换时:
<dataset>
<title>My tile</title>
</dataset>
生成了想要的结果:
<dataset>
<title>My tile</title>
</dataset>
当它应用于第二个XML文档时:
<dataset>
<title> </title>
</dataset>
产生了正确的结果:
ERROR:
<title> is empty
答案 1 :(得分:0)
我不清楚你真正想要的是什么。你说你不想发出包含空字符串的元素,然后给出一个例子:
<dataset>
<title> </title>
</dataset>
其中title
元素不包含空字符串。它包含空格。所以我假设“空字符串”你的意思是“只有空格。”
在处理之前,使用xsl:strip-space
将从源树中消除仅空白文本节点。如果您真的想要在遇到包含空格的元素时使用异常中止转换,则不能使用xsl:strip-space
,因为它会在转换运行之前删除所有异常触发条件。
我认为你要做的是写一个看起来像这样的模板:
<xsl:template match="*[not(*) and text() and not(normalize-space(text()) != '')]">
...
此模板将匹配以下内容为真的任何元素:
因此,在您的示例中,它与dataset
元素不匹配(因为它具有子元素),但它将匹配title
元素。但是,它不匹配<title/>
或<title></title>
,因为这些元素都不包含文本节点。
答案 2 :(得分:0)
也许测试应该像
length(text())!=0 && length(strip-whitespace(text())) == 0
xslt不支持正则表达式吗?如果是这样,那么这就是要走的路。
但他是否希望每个元素必须包含一些非空格文本?或者是否有一些元素必须包含至少某些内容和其他元素
<foo bar="BAR"/>
好吗?我敢打赌它是什么。我认为他很可能必须逐案编写那些必须非空的元素。
这引出了我的最后评论:检查XML文档的有效性的正确技术是XML模式。