XSLT样式表用空配对标记替换自闭合标记

时间:2011-02-17 17:27:17

标签: xslt

我正在使用XSLT处理我的ASP.Net web.config文件以插入一些额外的log4net配置。它由名为<style>的NANT标准任务应用。在成功插入新内容的同时,它会将许多自动关闭标记转换为空配对标记。例如,部分web.config在此之前如下所示:

<?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>

应用样式表后,<section><add>代码(以及所有其他代码)不再自动关闭:

<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>

我的样式表如下所示:

<?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>

在使用NANT处理样式表之前,我尝试使用MSBuild扩展包任务XmlTask进行MSBuild。它保留了自动关闭标签,但会丢失大部分换行符,这使得文件难以理解(尽管其他方面是正确的)。使用NANT非常适合我的构建过程,所以如果可以的话,我更愿意使用它。

似乎我应该能够指定我想在样式表中保留自闭标签,但我无法弄清楚如何。

4 个答案:

答案 0 :(得分:11)

自闭标记<empty/>和带有开始和结束标记<empty></empty>的空元素在语义上是相同的。因此,XSLT处理器可以输出最佳效果。

您可以尝试通过在元素中添加空内容来欺骗处理器。在这种情况下,可以通过修改身份模板来完成。

<!-- 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>

或者您可以通过保留原始身份模板并添加以下内容来限制此元素为空元素:

<!-- 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>

功能取决于XSLT处理器。

注意:正确的XML工具应该在自闭标签或带有开始和结束标签的空元素之间没有区别。因此,如果这种语法差异确实导致您出现问题,您应该重新考虑使用哪些方法或工具或如何使用它们。


更新

垃圾。不知怎的,我继续以你的意思相反的方式阅读你的问题(可能意味着我该睡觉的时间)。所以...上面的代码试图将自闭合标签转换为空对,而你想要相反的<tag></tag> - &gt; <tag/>。抱歉不匹配,让我再试一次。

在评论中你说:

  

在它出现在新行之前并缩进到与开始标记相同的位置。

从XML数据模型的角度来看,这意味着该节点只有空白空间作为子内容。尝试避免复制这些文本节点的一种方法是为它们提供空模板。但是,这可能会导致混合内容出现问题。

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

另一种可能的解决方案是,当我们面对空元素时,我们会创建一个具有相同名称的新元素,而不是复制它。

<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>

此模板还会在空元素中复制(未使用的)命名空间定义,这实际上似乎非常不必要。仅使用<xsl:for-each>代替<xsl:apply-templates>,因为template match不允许使用namespace轴。如果您不想保留额外的命名空间定义,<xsl:apply-templates select="@*"/>也可以工作。

但最终,AFAIK所有这些只是处理器特定的解决方法。当XSLT 1.0处理器创建一个空元素时,可以自由选择是使用自闭标签还是空对。如果我错了,请有人纠正我。

答案 1 :(得分:5)

只需在元素中添加注释,它就不会自行关闭。

  

&LT; XSL:评论&GT; &LT; / XSL:评论&GT;

答案 2 :(得分:1)

为什么不使用

<xsl:output method="html" />

答案 3 :(得分:0)

大多数XSLT处理器会将输出中的空元素序列化为<x/>而不是<x></x>。我不清楚你正在使用哪种XSLT处理器,或者它为什么不这样做,但它完全在其权利范围内 - 这两种结构是100%等效的,任何正确使用XML的人都不关心使用哪种形式。