如何在XSLT中避免生成空属性?

4

我已经编写了以下代码的XSLT文件:

<xsl:attribute name="subCode">
    <xsl:value-of select="Field[@key='XY']"/>
</xsl:attribute>

假设我的输入XML文件如下所示:

假设我的输入XML文件如下所示:

[...]
<Field key="XY"/>
[...]

在这种情况下,我的XSLT将生成以下输出结果:
<SomeElement subCode="">
[...]
</SomeElement>

我的问题是:如何去掉空属性subCode=""
我知道可以使用一些条件指令,比如<xsl:if>,但这似乎是一个丑陋的解决方案(因为我的XSLT中生成了数千个类似的属性)。
必须在同一个XSLT中完成。我不能在额外的XSLT文件中添加后处理。
除此之外,输出的XML已经定义了其XSD模式。模式表明该属性是可选的。也许有一些方法可以将XSD模式应用于输出的XML?
谢谢您提前的帮助!

不错的问题,+1。请看我的答案,这是所有答案中最短、最精确的完整解决方案。 - Dimitre Novatchev
3个回答

4

用途:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*">
  <someElement>
    <xsl:apply-templates mode="attr"/>
  </someElement>
 </xsl:template>

 <xsl:template match="Field[@key='XY' and not(.='')]" mode="attr">
    <xsl:attribute name="subCode">
      <xsl:value-of select="."/>
    </xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

应用此转换时,以下 XML 文档将被转换:
<t>
 <Field key="XY"/>
</t>

期望的正确结果是不生成任何属性:

当源XML文档如下时:

<t>
 <Field key="XY">1</Field>
</t>

相同的转换再次产生正确的、期望的结果;
<someElement subCode="1"/>

+1 这是正确的:测试元素的字符串值。其他谓词将是 [string()][normalize-space()] - user357812
1
@Alejandro,你提出的两个谓词对于字符串“0”都会返回false() - Dimitre Novatchev

1

您可以定义一个模板来匹配属性,这将为缺少的@key提供正确的输出,但如果您还添加谓词[.!=''],则应该忽略没有值的属性。

<xsl:template match="@key[.!='']">
  <xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>

或者在你的例子中,如果你只想匹配@key='XY',可以使用:

<xsl:template match="@key[.='XY']">
  <xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>

编辑:这是我用来测试的更完整的示例。

源XML

<Fields>
   <Field key="XY">A</Field>
   <Field key="XY">B</Field>
   <Field key="">C</Field>
   <Field>D</Field>
</Fields>

伴随的 XSL

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
  <output>
    <xsl:apply-templates />
  </output>
</xsl:template>

<xsl:template match="Field">
  <xsl:element name="NewField">
    <xsl:apply-templates select="@*"/>
    <xsl:value-of select="."/>
  </xsl:element>
</xsl:template>

<xsl:template match="@key[.='XY']">
  <xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>

结果

<output>
  <NewField subCode="XY">A</NewField>
  <NewField subCode="XY">B</NewField>
  <NewField>C</NewField>
  <NewField>D</NewField>
</output>

我认为原帖的问题并不是要求属性字符串值,而是要求元素字符串值。 - user357812
啊,是的,我有点误解了这个问题! - andyb

0
您应该将以下模板应用于属性:
此模板匹配所有键的属性,其值为“XY”:
<xsl:template match="@key['XY']">
    <xsl:attribute name="subCode">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

此模板匹配所有关键属性,其(文本)值长度大于0:

<xsl:template match="@key[string-length(.)]">
    <xsl:attribute name="other">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

最后,此模板匹配所有空属性(*):

<xsl:template match="@*[not(string-length(.))]" />

所以,如果您有像这样的输入

<?xml version="1.0"?>
<Root>
    <Field key='v'>One</Field>
    <Field key='XY'>Two</Field>
    <Field key='a'>Three</Field>
    <Field key='v'>Four</Field>
    <Field key='XY'>Five</Field>
    <Field>Six</Field>
    <Field>Seven</Field>
    <Field key='b'>Eight</Field>
    <Field key=''>Nine</Field>
    <Field></Field>
    <Field key='x'>Eleven</Field>
    <Field key=''>Twelve</Field>
    <Field lat='x'>Thirteen</Field>
</Root>

你可以生成一个输出:

<?xml version='1.0' ?>
<Root>
    <SomeField other="v">One</SomeField>
    <SomeField subCode="XY">Two</SomeField>
    <SomeField other="a">Three</SomeField>
    <SomeField other="v">Four</SomeField>
    <SomeField subCode="XY">Five</SomeField>
    <SomeField>Six</SomeField>
    <SomeField>Seven</SomeField>
    <SomeField other="b">Eight</SomeField>
    <SomeField>Nine</SomeField>
    <SomeField/>
    <SomeField other="x">Eleven</SomeField>
    <SomeField>Twelve</SomeField>
    <SomeField>xThirteen</SomeField>
</Root>

使用以下XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <Root>
            <xsl:apply-templates/>
        </Root>
    </xsl:template>

    <xsl:template match="Field">
        <xsl:element name="SomeField">
            <xsl:apply-templates select="@*"/>
            <xsl:value-of select="text()"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@key['XY']">
        <xsl:attribute name="subCode">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="@key[string-length(.)]">
        <xsl:attribute name="other">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="@key[not(string-length(.))]" />
</xsl:stylesheet>

谢谢,你在回答中付出了很多努力。然而,我感觉我没有清楚地表达我想要实现的目标。我想要的只是跳过生成空属性。 - Lukasz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接