Таблица стилей XSLT заменяет самозакрывающиеся теги пустыми парными тегами - PullRequest
8 голосов
/ 17 февраля 2011

Я использую XSLT для обработки файла ASP.Net web.config для вставки дополнительной конфигурации log4net. Он применяется стандартной задачей NANT под названием <style>. Успешно вставляя новый контент, он превращает множество самозакрывающихся тегов в пустые парные теги. Например, неполный файл 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, используя задачу пакета расширений MSBuild XmlTask. Он сохранил самозакрывающиеся теги, но потерял бы большую часть разрывов строк, что делало файл нечитаемым (хотя в противном случае он был бы правильным) Использование NANT прекрасно вписывается в мой процесс сборки, поэтому я бы предпочел использовать его, если смогу.

Похоже, я должен быть в состоянии указать, что я хочу сохранить самозакрывающиеся теги в таблице стилей, но не могу понять, как.

Ответы [ 4 ]

11 голосов
/ 17 февраля 2011

Самозакрывающиеся теги <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> -> <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 создает пустой элемент, он может свободно выбирать, использовать ли самозакрывающийся тег или пустую пару. Кто-нибудь, пожалуйста, поправьте меня, если я ошибаюсь.

5 голосов
/ 27 июня 2012

Просто добавьте комментарий в элемент, он не закроется самостоятельно.

</ xsl: comment>

1 голос
/ 13 марта 2013

Почему бы не использовать

<xsl:output method="html" />
0 голосов
/ 18 февраля 2011

Большинство процессоров XSLT сериализуют пустой элемент в выводе как <x/>, а не <x></x>.Мне не ясно, какой процессор XSLT вы используете, или почему он этого не делает, но он полностью в пределах своих прав - эти две конструкции на 100% эквивалентны, и любой, кто правильно использует XML, не будет заботиться о том, какая форма используется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...