This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
RE: How to delete empty element tag from output XML?
- To: "Guangzu Wang (Houston)" <Guangzu dot Wang at channelinx dot com>
- Subject: RE: How to delete empty element tag from output XML?
- From: Jeni Tennison <mail at jenitennison dot com>
- Date: Fri, 27 Oct 2000 10:48:30 +0100
- Cc: xsl-list at mulberrytech dot com
- Reply-To: xsl-list at mulberrytech dot com
Guangzu,
>Thanks to Kay Michael and Jeni Tennison for your help. A general template is
>what I am looking for. I wrote a template, it's more like a C function
>except more awkward. Most of the codes is for print tag. I wish I can use
><xsl:element> to print the tag which is passed by parameter but don't know
>how to assign the parameter to the name of element.
With xsl:element, the @name attribute can take an attribute value template,
so you can give computed values to the name of the element if you wrap the
computed (part of) the name in {}s:
<xsl:element name="{$tag}">
...
</xsl:element>
You can do the same with attribute names as well. Actually, I used this in
the template in my previous message:
<xsl:template match="*">
<xsl:variable name="translation" select="..." />
<xsl:variable name="default" select="..." />
<xsl:choose>
<xsl:when test="string(.)">
<!-- element added with the name given by $translation variable -->
<xsl:element name="{$translation}">
<xsl:value-of select="." />
</xsl:element>
</xsl:when>
<xsl:when test="string($default)">
<xsl:element name="{$translation}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
If you apply this to your template, then you get:
<xsl:template name="empty_content">
<xsl:with-param name="content" />
<xsl:with-param name="tag" />
<xsl:with-param name="default" />
<xsl:if test="$content !=''">
<xsl:element name="{$tag}">
<xsl:value-of select="$content" />
</xsl:element>
</xsl:if>
<xsl:if test="$content = ''">
<xsl:if test="$default != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:if>
</xsl:if>
</xsl:template>
You can see that this is a lot cleaner than all those xsl:texts. I don't
know what the xsl:with-params are doing there - they're illegal at that
point (they're designed for being with an xsl:call-template or an
xsl:apply-templates, to *pass* parameters). You meant xsl:param:
<xsl:template name="empty_content">
<xsl:param name="content" />
<xsl:param name="tag" />
<xsl:param name="default" />
<xsl:if test="$content !=''">
<xsl:element name="{$tag}">
<xsl:value-of select="$content" />
</xsl:element>
</xsl:if>
<xsl:if test="$content = ''">
<xsl:if test="$default != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:if>
</xsl:if>
</xsl:template>
There are some other changes that you could make if you wanted to, and I'll
step through them so that you can see the alternatives. When you have two
xsl:ifs that test mutually-exclusive conditions side by side (as you do
here), then you can turn it into an xsl:choose with an xsl:when and an
xsl:otherwise instead:
<xsl:template name="empty-content">
<xsl:param name="content" />
<xsl:param name="tag" />
<xsl:param name="default" />
<xsl:choose>
<xsl:when test="$content != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$content" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$default != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
When you have a xsl:otherwise with an xsl:if inside it as the only content,
then you can turn it into an xsl:when with the test from the xsl:if to give
the same effect:
<xsl:template name="empty-content">
<xsl:param name="content" />
<xsl:param name="tag" />
<xsl:param name="default" />
<xsl:choose>
<xsl:when test="$content != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$content" />
</xsl:element>
</xsl:when>
<xsl:when test="$default != ''">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
When you are testing whether a string ($content and $default are both
strings) has content or not, you can use the fact that strings are true()
if they have content, and false() if they don't. So within a test (which
automatically interprets its contents as a boolean expression):
$content != ''
is equivalent to simply:
$content
And the template can alternatively be:
<xsl:template name="empty-content">
<xsl:param name="content" />
<xsl:param name="tag" />
<xsl:param name="default" />
<xsl:choose>
<xsl:when test="$content">
<xsl:element name="{$tag}">
<xsl:value-of select="$content" />
</xsl:element>
</xsl:when>
<xsl:when test="$default">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
When you're passing parameters that include things about the content or
name of a node, then you may as well change the template to a moded
template that matches that node, and work out the content/name within the
template. This saves on xsl:with-params, which are very verbose:
<xsl:template match="people">
<Star>
<FirstName><xsl:value-of select="N1" /></FirstName>
<LastName><xsl:value-of select="N2" /></LastName>
<xsl:apply-templates select="N3" mode="empty_content">
<xsl:with-param name="tag">MI</xsl:with-param>
<xsl:with-param name="default">N/A</xsl:with-param>
</xsl:call-template>
<xsl:call-template select="Sex" mode="empty_content">
<xsl:with-param name="tag">Gender</xsl:with-param>
<xsl:with-param name="default"></xsl:with-param>
</xsl:call-template>
</Star>
</xsl:template>
<xsl:template match="*" mode="empty-content">
<xsl:param name="tag" />
<xsl:param name="default" />
<xsl:choose>
<xsl:when test="string(.)">
<xsl:element name="{$tag}">
<xsl:value-of select="." />
</xsl:element>
</xsl:when>
<xsl:when test="not($default)">
<xsl:element name="{$tag}">
<xsl:value-of select="$default" />
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
A final point - if you're worried about the formatting of your output, then
I suggest you use:
<xsl:output indent="yes" />
near the top of your stylesheet. This will make the XSLT processor indent
things sensibly for you.
Cheers,
Jeni
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list