Reputation: 7662
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
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
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
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