Lukasz
Lukasz

Reputation: 7662

How to avoid generation of empty attributes in XSLT?

I have written XSLT file with the following code:

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

Let's say that my INPUT XML looks like this:

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

In this case my XSLT would generate the following output:

<SomeElement subCode="">
[...]
</SomeElement>

My question is: How to ged rid of empty attribute subCode=""?

I know that it is possible by using some conditional instruction like <xsl:if>, however, this seems to be an ugly solution (because I have thousands of similar attributes generated in my XSLT).

It must be done in the same XSLT. I cannot add post-processing in additional XSLT file.

Besides that, the output XML has got its XSD Schema defined. And the schema says that this attribute is optional. Maybe there is some way to apply that XSD schema for the output XML?

Thanks in advance for any help!

Upvotes: 4

Views: 4051

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

Use:

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

when this transformation is applied on the following XML document:

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

the wanted, correct result (no attribute at all is generated) is produced:

when the source XML document is this:

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

the same transformation again produces the correct, wanted result;

<someElement subCode="1"/>

Upvotes: 4

rekaszeru
rekaszeru

Reputation: 19220

You should apply the following templates to your attributes:

This template matches all key the attributes who's value is 'XY':

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

This template matches all the key attributes with a (text)value longer than 0:

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

And finally this template matches all the attributes (*) that are empty:

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

So if you have an input like

<?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>

You can generate an output:

<?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>

using the following 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>

Upvotes: 0

andyb
andyb

Reputation: 43823

You can define a template to match the attribute which will give you the correct output for a missing @key but if you also add the predicate [.!=''] it should then also ignore the attributes with no value.

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

Or in your example if you only want the @key='XY' matches use:

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

Edit: Here is a more complete example that I used to test this.

Source XML

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

Accompanying 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>

Result

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

Upvotes: 1

Related Questions