XML со встроенным XSLT - PullRequest
       1

XML со встроенным XSLT

3 голосов
/ 27 октября 2010

Мне нужно изменить XML-файл после установки, основываясь на некоторых параметрах.

Например:

<?xml ...?>
<root xmlns="...">

  <!-- remove this element when PARAM_MODE=1 -->
  <sometag />

  <!-- remove this element when PARAM_MODE=2 -->
  <sometag2 />

  <someothertag />
</root>

Самый простой способ использовать XSLT для удаления элемента, но я хочу, чтобы комментарий и XSL были объединены и не дублировались.

Есть ли что-то, что может это сделать? Примерно так:

<?xml ...?>
<root xmlns="..." xmlns:xsl="XSL_NAMESPACE">
  <!-- XSL to remove this element when PARAM_MODE=1 -->
  <xsl:remove the attribute if $PARAM_MODE=1 />
  <sometag />

  <!-- XSL to remove this element when PARAM_MODE=2 -->
  <xsl:remove the attribute if $PARAM_MODE=2 />
  <sometag2 />

  <someothertag />
</root>

Ответы [ 3 ]

3 голосов
/ 27 октября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="...">
    <xsl:param name="PARAM_MODE" select="1"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="my:sometag">
        <xsl:if test="$PARAM_MODE!=1">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
    <xsl:template match="my:sometag2">
        <xsl:if test="$PARAM_MODE!=2">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

С этим входом:

<root xmlns="...">
    <!-- remove this element when PARAM_MODE=1 -->
    <sometag />
    <!-- remove this element when PARAM_MODE=2 -->
    <sometag2 />
    <someothertag />
</root>

Выход:

<root xmlns="...">
    <!-- remove this element when PARAM_MODE=1 -->
    <!-- remove this element when PARAM_MODE=2 -->
    <sometag2></sometag2>
    <someothertag></someothertag>
</root>

Обратите внимание, что если вам нужен упрощенный синтаксис , из http://www.w3.org/TR/xslt#result-element-stylesheet:

Упрощенный синтаксис допускается для таблицы стилей, которые состоят только из один шаблон для корневого узла. таблица стилей может состоять только из элемент буквального результата (см. 7.1.1 Элементы буквального результата ). Такой таблица стилей эквивалентна таблица стилей с помощью xsl: stylesheet элемент, содержащий шаблонное правило содержащий буквальный элемент результата; шаблонное правило имеет шаблон соответствия /.

Таким образом, вы можете добавлять элементы, но не можете удалять их.

EDIT : обратная логика для упрощенного синтаксиса.

Предположим, эта таблица стилей с ... test.xsl URI:

<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<root 
 xmlns="..." 
 xmlns:my="..." 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
 xsl:version="1.0">
    <PARAM_MODE>1</PARAM_MODE>
    <!-- remove this element when PARAM_MODE=1 -->
    <xsl:if test="document('')/my:root/my:PARAM_MODE!=1">
        <sometag />
    </xsl:if>
    <!-- remove this element when PARAM_MODE=2 -->
    <xsl:if test="document('')/my:root/my:PARAM_MODE!=2">
        <sometag2 />
    </xsl:if>
    <someothertag />
</root>

Runnig с самим собой в качестве ввода (я подчеркиваю это с помощью PI. Кроме того, это делает fn:document() лишним ...), он выводит:

<root xmlns="..." xmlns:my="...">
    <PARAM_MODE>1</PARAM_MODE>
    <sometag2 />
    <someothertag />
</root>

Наконец, таблица стилей, управляемая комментариями:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="PARAM_MODE" select="1"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[preceding-sibling::node()[1]
                            /self::comment()[starts-with(.,' remove ')]]">
        <xsl:if test="$PARAM_MODE != substring-after(
                                        preceding-sibling::comment()[1],
                                        'PARAM_MODE=')">
            <xsl:call-template name="identity"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Выход:

<root xmlns="...">
    <!-- remove this element when PARAM_MODE=1 -->
    <!-- remove this element when PARAM_MODE=2 -->
    <sometag2></sometag2>
    <someothertag></someothertag>
</root>
1 голос
/ 27 октября 2010

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

Используя этот вход:

<?xml version="1.0" encoding="utf-8" ?>
<tle>
  <!-- remove this element if $param = 1-->
  <foo/>
  <dont_remove/>
  <dont_remove/>
  <!-- remove this element if $param = 2-->
  <bar/>
  <dont_remove/>
  <dont_remove/>
  <dont_remove/>
</tle>

и это преобразование:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="param">2</xsl:variable>

  <xsl:template match="@* | node()">
      <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
      </xsl:copy> 
  </xsl:template>

  <!-- this matches only elements whose immediately preceding non-text node is
       a comment; all other elements are copied to the output by the identity
       template.  

       it ignores text nodes so that whitespace between the comment and the
       element it's tagging doesn't break their association.
  -->
  <xsl:template match="match="*[preceding-sibling::node()
                                 [not(self::text())]
                                 [1]
                                 [self::comment()]
                               ]">
    <!-- find the immediately preceding comment -->
    <xsl:variable name="comment" select="preceding-sibling::comment()[1]"/>
    <!-- don't copy this element if the text of the comment matches the 
         value of $param -->
    <xsl:choose>
      <xsl:when test="$param = 1 and contains($comment, 'param = 1')"/>
      <xsl:when test="$param = 2 and contains($comment, 'param = 2')"/>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

производит этот вывод:

<tle>
  <!-- remove this element if $param = 1-->
  <foo />
  <dont_remove />
  <dont_remove />
  <!-- remove this element if $param = 2-->

  <dont_remove />
  <dont_remove />
  <dont_remove />
</tle>
1 голос
/ 27 октября 2010

Эта таблица стилей использует значения comment и param, чтобы определить, какой контент нужно редактировать.

Вдохновение для XPATH для поиска подходящего элемента было получено из ответа @ Dimitre Novatchev для xslt и xpath: совпадение с предыдущими комментариями .

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>

    <xsl:param name="PARAM_MODE" />

    <!--Match on the comments to remove elements, and the elements immediately following -->

    <xsl:template match="comment()[starts-with(.,' remove this element when PARAM_MODE=')] 
         | 
         *[self::*[
                generate-id(
                     preceding-sibling::comment()
                       [1]
                       [starts-with(.,' remove this element when PARAM_MODE=')]
                     /following-sibling::node()
                       [1]
                       [self::text() and not(normalize-space())]
                     /following-sibling::node()
                       [1]
                       [self::*]
                 )=generate-id()
            ]
         ]">

        <!--Param/Variable references are not allowed in the match expression, so we will need to do a little work inside of the template -->
        <xsl:choose>
           <!--If the PARAM_MODE value matches the comment, or it's the element immediately following, then remove it -->
            <xsl:when test="(self::comment() and substring-after(.,'=')=$PARAM_MODE) 
                or 
                (self::*[
                        generate-id(
                            preceding-sibling::comment()
                               [1]
                               [starts-with(.,' remove this element when PARAM_MODE=') 
                                  and substring-after(.,'=')=$PARAM_MODE]
                            /following-sibling::node()
                               [1]
                               [self::text() and not(normalize-space())]
                            /following-sibling::node()
                               [1]
                               [self::*]
                          )=generate-id()
                    ])" />
            <xsl:otherwise>
                <!--Otherwise, invoke the identity template and copy forward -->
                <xsl:call-template name="identity"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

<!--Identity template that copies content into the result document -->
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

При применении к этому документу XML (с параметром PARAM_MODE = 2 ):

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="...">

    <!-- remove this element when PARAM_MODE=1 -->
    <sometag />

    <!-- remove this element when PARAM_MODE=2 -->
    <sometag2 />

    <someothertag />
</root>

Производится следующий вывод:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="...">

    <!-- remove this element when PARAM_MODE=1 -->
    <sometag/>




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