XSLT Style Sheet Replaces Self-Closing Tags with Empty Paired Tags

I use XSLT to process my ASP.Net web.config file to insert an additional log4net configuration. It is used by the standard NANT task called <style> . Although it successfully inserts new content, it turns many self-closing tags into empty paired tags. For example, a partial web.config looks like this:

 <?xml version="1.0"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <appSettings> <add key="SomeKey" value="SomeValue"/> </appSettings> 

After applying the stylesheet, the <section> and <add> tags (and all other tags) are no longer self-closing:

 <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"> </section> </configSections> <appSettings> <add key="SomeKey" value="SomeValue"> </add> </appSettings> 

My stylesheet looks like this:

 <?xml version="1.0" encoding="utf-8"?> <!-- This stylesheet is applied to web.config files to insert log4net appender filters that will prevent logging messages resulting from pages requested by AIS monitoring systems. --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:output method="xml" indent="yes" /> <xsl:preserve-space elements="configuration"/> <!-- Copy input to output, most of the time --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()" /> </xsl:copy> </xsl:template> <!-- Within log4net <appender> elements, insert standard filters to exclude logging traffic resulting from AIS monitoring. Any existing filters are preserved. --> <xsl:template match="/configuration/log4net/appender"> <xsl:copy> <xsl:apply-templates select="@* | node()" /> <xsl:comment > Filters inserted by build server during deployment </xsl:comment> <filter name="AIS monitor" type="log4net.Filter.PropertyFilter"> <regexToMatch value="^35\.8\.113\.[0-9]+$"/> <key value="ClientIP"/> <acceptOnMatch value="false"/> </filter> <filter name="AIS load balancer" type="log4net.Filter.PropertyFilter"> <regexToMatch value="^10\.160\.0\.[0-9]+$" /> <key value="ClientIP"/> <acceptOnMatch value="false"/> </filter> <filter name="localhost" type="log4net.Filter.PropertyFilter"> <stringToMatch value="127.0.0.1"/> <key value="ClientIP"/> <acceptOnMatch value="false"/> </filter> </xsl:copy> </xsl:template> </xsl:stylesheet> 

Before using NANT to process the stylesheet, I tried MSBuild using the XmlTask Extension Pack XmlTask . He retained the self-closing tags, but would have lost most of the line breaks, which made the file believable (although otherwise true). Using NANT fits nicely into my build process, so I'd rather use it if I can.

It seems that I should be able to indicate that I want to save self-closing tags in the stylesheet, but I cannot figure out how to do this.

+8
xslt
source share
4 answers

Self-closing <empty/> tags and an empty element with start and end <empty></empty> tags are semantically identical. Therefore, the XSLT processor can output depending on what it sees better.

You can try to trick the processor by adding empty content to the elements. In this case, this can be done by changing the identity template.

 <!-- Define a dummy variable with empty content --> <xsl:variable name="empty" select="''"/> <!-- Copy input to output, most of the time --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()" /> <!-- Insert empty content into copied element --> <xsl:value-of select="$empty"/> </xsl:copy> </xsl:template> 

Or you can restrict this to an empty element by keeping the original identification template and adding the following:

 <!-- Identity template for empty elements --> <xsl:template match="*[not(node())]"> <xsl:copy> <xsl:apply-templates select="@* | node()" /> <xsl:value-of select="$empty"/> </xsl:copy> </xsl:template> 

Functionality is dependent on the XSLT processor.

Note. . A valid XML tool should not distinguish between self-closing tags or empty elements with start and end tags. Therefore, if this difference in syntax really causes problems, you should review what methods or tools you use or how you use them.


Update

Shit. For some reason, I continued to read your question in the opposite way (which probably means I should take a nap). So ... the code above is trying to convert self-closing tags to empty pairs, whereas you would like the opposite <tag></tag> β†’ <tag/> . Sorry for the inconsistency, let me try again.

In the comment you said:

Before he was on a new line and indented to the same place as the opening tag.

From the point of view of the XML data model, this means that the node has only an empty space as child content. One way to avoid copying these text nodes is to provide them with an empty template. This can cause mixed content issues.

 <xsl:template match="text()[normalize-space() = '']"/> 

Another possible solution would be that when we come across empty elements, we create a new element with the same name and not copy it.

 <xsl:template match="*[not(comment() | processing-instruction() | *)][normalize-space(text()) = '']"> <xsl:element name="{name()}" namespace="{namespace-uri()}"> <xsl:for-each select="@* | namespace::*"> <xsl:copy/> </xsl:for-each> </xsl:element> </xsl:template> 

This template also copies the (unused) namespace definitions into empty elements, which actually seems completely unnecessary. <xsl:for-each> used instead of <xsl:apply-templates> only because template match does not allow the namespace axis to be used. <xsl:apply-templates select="@*"/> also works if you do not want to save additional namespace definitions.

But in the end, AFAIK are all just processor-specific workarounds. When an XSLT 1.0 processor creates an empty element, it is free to choose whether to use a self-closing tag or an empty pair. Someone please correct me if I am wrong.

+11
source share

Just add a comment to the element, it will not be closed by itself.

<XSL: Comment> </ XSL: Comment>

+5
source share

Why not use

 <xsl:output method="html" /> 
+1
source share

Most XSLT processors serialize an empty output element as <x/> rather than <x></x> . I don’t understand which XSLT processor you use, or why it does not, but it is completely within its rights - these two constructions are 100% equivalent, and anyone who consumes XML will not care about which form is used.

0
source share

All Articles