Link to home
Start Free TrialLog in
Avatar of creditpointe
creditpointe

asked on

XSL to replace <tag/> with <tag></tag>

The requirement is that, all the empty tags in the transformed XML:should look like <tag></tag> and not <tag/>
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Forget it, it can't be done in XSLT.

But basically, any application that has a preference, is not a real XML application, it should not make a difference

From the XML point of view <tag/> and <tag></tag> are equivalent,
and the serialisation engine of your XSLT processor can choose one of both options when serialising.
Most choose the short form, it is not documented and even if you would find a processor that does it the way you want, you can't guarantuee that it will not break in the next version

There are a number of non documented things you can try

1. If it is allowed to put something in between the tags, the tags will be broken
This could be a comment, a space or a process instruction
<tag><xsl:comment>*</xsl:comment></tag>
will generate something like this
<tag><!-- * --></tag>
your follow up XML process should ignore that comment and it could have the same effect

2. If it is not allowed to have something in between the tags, use a post process.
I have a customer who built his own XML loader that puts stuff I send him in a database
He has built his own parser and it is crappy, so it doesn't take <tag/>
For him I use this technique
I create <tag><!-- * --></tag>
and in a post process I use regular expressions that simple remove all the <!-- * --> from the document
This works really well

3. There is one obscure technique that helps with some processors. But it is undocumented and I can't guarantuee that it won't break in one of the next versions. Basically you try to throw something in between the two tags that won't get serialised at the end.
This for example works in Xalan (for Java)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:variable name="empty"/>
 <xsl:template match="tag">
     <tag>
         <xsl:value-of select="$empty"/>
     </tag>
 </xsl:template>
</xsl:stylesheet>
I bet you could find similar constructs that would break the tag in Saxon as well... but these are ugly hacks
I used that for a while, but I strongly recommend not to use dirty hacks like this... the will break at some point

So,
first evaluate if you really need this. If there is a real need it would be because the next step in the pipeline is in default.
There should not be a requirement for what you ask with real XML applications
then evaluate whether it is allowed to put a space or a comment in between,
If you can't put a comment or so in between, still do, but remove it with regular expressions in a post process

I hope this helps you

Geert

Avatar of creditpointe
creditpointe

ASKER

Thanks Gertone for providing so many options.
The first solution is very interesting.
Allow me to ask a few more questions and add something more to my question.

Is there any way which would allow to put an empty space for all the tags (for the complete inbound xml) processed where there is no text? In other words, if the source xml has <tag></tag> (for any tag across the inbound xml) then the output would consist of <tag> "space" </tag>?
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
      <xsl:template match="/">
      <xsl:apply-templates select="*"/>
      </xsl:template>

      <xsl:template match="*">
      <xsl:choose>
      <xsl:when test=". != ''">
      <xsl:element name="{name()}">
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
      </xsl:element>
      </xsl:when>
<xsl:otherwise>
<xsl:element name="{name()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
<xsl:comment>empty element</xsl:comment>
</xsl:element>
</xsl:otherwise>
</xsl:choose>

</xsl:template>

</xsl:stylesheet>
use this it will work.
@avsviswanath

That does not really do what the original poster asked,
and was suggested as the first option in the first comment

I add this just in case someone finding this answer in the database thinks that something new was added, after an answer was accepted