如何找到xsl中具有空字符串值的元素节点

时间:2009-02-13 00:05:05

标签: java xml xslt xpath xsd

我正在努力将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处理器。

3 个答案:

答案 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>

有三个文字节点

  1. 第一个文本节点是<dataset><title>之间的节点,它只包含空格。

  2. 第二个文本节点是<title>的唯一子节点,其值为字符串“My tile”。

  3. 第三个和最后一个文本节点位于</title></dataset>之间,仅包含空格。

  4. 当选择匹配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:        
          &lt;<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模式。