How to preserve Empty XML Tags after XSLT - prevent collapsing them from <B></B> to <B/>
Asked Answered
S

11

14

Say I have a very simple XML with an empty tag 'B':

<Root>
  <A>foo</A>
  <B></B>
  <C>bar</C>
</Root>

I'm currently using XSLT to remove a few tags, like 'C' for example:

<?xml version="1.0" ?>

<xsl:stylesheet version="2.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="no" encoding="utf-8" omit-xml-declaration="yes" />

<xsl:template match="*">
    <xsl:copy>
        <xsl:copy-of select="@*" />
        <xsl:apply-templates />
    </xsl:copy>
</xsl:template>

<xsl:template match="C" />

</xsl:stylesheet>

So far OK, but the problem is I end up having an output like this:

<Root>
  <A>foo</A>
  <B/>
</Root>

when I actually really want:

<Root>
  <A>foo</A>
  <B></B>
</Root>

Is there a way to prevent 'B' from collapsing?

Thanks.

Sturgis answered 11/6, 2009 at 8:43 Comment(4)
I've just realized I can trick the XSL with by setting the output method to HTML: xsl:output method="html" Therefore, I end up having B not collapsed as output. Do you guys see a problem with this solution?Sturgis
I'm not sure why you want that. "<B/>" and "<B></B>" are absolutely equivalent. If you rely on "</B>" you are doing something wrong.Greenock
No, I'm not doing wrong. I have to deal with an external provider which fails handling <B/>, so since I can't force him to fix this I have to live with that.Sturgis
Okay. So they are doing something wrong. :)Greenock
S
11

Ok, so here what worked for me:

<xsl:output method="html">
Sturgis answered 11/6, 2009 at 12:47 Comment(2)
Just method="html" and no other change gives <B></B> instead of <B />?Expressly
NB. This won't preserve input, it will also open all empty tags so <x/> in input turns into <x></x>Hamilton
K
3

Try this:

<script type="..." src="...">&#160;</script>

Your HTML output will be:

<script type="..." src="..."> </script>

The &#160; prevents the collapsing but translates to a blank space. It's worked for me in the past.

Kwa answered 1/6, 2011 at 19:11 Comment(3)
+1 for the way around. This will work with XML output method also.Tiruchirapalli
+1 - worked as workaround for the broken XSLT processor. Thank you.Glynnis
Dangerous approach, since this results in an element that has actual text content and is thus no longer equivalent to an empty element.Reine
J
2

There is no standard way, as they are equivalent; You might be able to find an XSLT engine that has an option for this behaviour, but I'm not aware of any.

If you're passing this to a third party that cannot accept empty tags using this syntax, then you may have to post-process the output yourself (or convince the third party to fix their XML parsing)

Jude answered 11/6, 2009 at 8:47 Comment(3)
See my comment above. It seems xsl:output method="html" would fix it.Sturgis
With some parsers, and some elements, that will completely omit the closing tag altogether; It can work, but isn't a general solutionJude
+1 re post processing. If you are outputting XHTML for the web, you still need to have empty elements with a close element for some browsers (e.g. <script... /> has to be <script ... ></script>) so not such an uncommon problem.Thaumaturge
C
2

It is up to the XSLT engine to decide how the XML tag is rendered, because a parser should see no difference between the two variations. However, when outputting HTML this is a common problem (for <textarea> and <script> tags for example.) The simplest (but ugly) solution is to add a single whitespace inside the tag (this does change the meaning of the tag slightly though.)

Collett answered 11/6, 2009 at 8:50 Comment(3)
It could work, but I can't afford modifying the original XML.Sturgis
Then your simplest option is to post-process the XSLT. The quick and dirty solution is to make a regex to replace <.../> with <...></...> (which many will frown upon because it's not a solid solution if you want to support any kind of XML.) The other, proper solution is to change the XSLT engine.Collett
What about setting the output method as HTML? I quick test I did prevented empty collapsed elements, but I'm not sure about possible side-effects...Sturgis
H
1

This has been a long time issue and I finally made it work with a simple solution.

Add <xsl:text/> if you have a space character. I added a space in my helper class.

<xsl:choose>
 <xsl:when test="$textAreaValue=' '">
   <xsl:text/>
 </xsl:when>
 <xsl:otherwise>
   <xsl:value-of select="$textAreaValue"/>
 </xsl:otherwise>
</xsl:choose>
Huntingdonshire answered 18/10, 2010 at 14:28 Comment(0)
E
1

They are NOT always equivalent. Many browsers can't deal with <script type="..." src="..." /> and want a separate closing tag. I ran into this problem while using xml/xsl with PHP. Output "html" didn't work, I'm still looking for a solution.

Excommunication answered 10/1, 2011 at 12:39 Comment(0)
J
0

No. The 2 are syntactically identical, so you shouldn't have to worry

Jeweller answered 11/6, 2009 at 8:47 Comment(2)
The problem is I'm passing this XML to a third party that does not accept collapsed elements (unfortunatelly).Sturgis
It sounds like they've rolled their own XML parser, in that case, and you have to wonder what else they won't accept. Proper character encodings ? Entities etc.?Jeweller
F
0

It should not be a problem if it is or . However if you are using another tool which expects empty XML tags as way only, then you have a problem. A not very elegant way to do this will be adding a space between staring and ending 'B' tags through XSLT code.

Finical answered 11/6, 2009 at 8:53 Comment(2)
As I said above, I can't modify the original XML.Sturgis
Another option for you then is write the empty elements through XSLT code like <xsl:text>&gt;B&lt;&gt;/B&gt;</xslt:text>Finical
D
0
<xsl:text disable-output-escaping="yes">
<![CDATA[<div></div>]]>
</xsl:text>

This works fine with C#'s XslCompiledTransform class with .Net 2.0, but may very well fail almost anywhere else. Do not use unless you are programmatically doing the transofrm yourself; it is not portable at all.

Dispersoid answered 25/8, 2009 at 14:27 Comment(0)
G
0

It's 7 years late, but for future readers I will buck the trend here and propose an actual solution to the original question. A solution that does not modify the original with spaces or the output directive.

The idea was to use an empty variable to trick the parser.

If you only want to do it just for one tag B, my first thought was to use something like this to attach a dummy variable.

<xsl:variable name="dummyempty" select="''"/>

<xsl:template match="B">    
  <xsl:copy>
    <xsl:apply-templates select="@*" />
    <xsl:value-of select="concat(., $dummyempty)"/>
  </xsl:copy>    
</xsl:template>

But I found that in fact, even the dummy variable is not necessary. This preserved empty tags, at least when tested with xsltproc in linux :

<xsl:template match="B">
  <xsl:copy>
    <xsl:apply-templates select="@*" />
    <xsl:value-of select="."/>
  </xsl:copy>    
</xsl:template>

For a more generic solution to handle ALL empty tags, try this:

  <xsl:variable name="dummyempty" select="''"/>

  <xsl:template match="*[. = '']">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*" />
        <xsl:value-of select="$dummyempty"/>
    </xsl:copy>
  </xsl:template>

Again, depending on how smart your parser is, you may not even need the dummy variable.

Gamali answered 26/7, 2016 at 2:48 Comment(0)
L
0

Following Amit's lead of being helpful 7 years later again, I found that as of today, with

:; xsltproc --version
Using libxml 20913, libxslt 10135 and libexslt 820
xsltproc was compiled against libxml 20913, libxslt 10135 and libexslt 820
libxslt 10135 was compiled against libxml 20913
libexslt 820 was compiled against libxml 20913

on a current macOS install, that the dummy variable or simple copy no longer fool xsltproc, but an empty comment does. In my case I wanted to preserve <string></string> and ended up with:

 <xsl:template match="string[.='']">
  <xsl:copy>
    <xsl:apply-templates select="@*" />
    <xsl:comment/>
  </xsl:copy>
</xsl:template>
Lovemaking answered 6/4 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.