This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

RE: How to delete empty element tag from output XML?


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]