Предотвращение 2 ^ n - 1 условных выражений в XSL 1.0 - PullRequest
2 голосов
/ 23 марта 2012

С учетом следующего XML:

<root>
    Pacman <format bold="1" italic="1">rules</format>!
</root>

Какая реализация лучше, чем следующая, что приводит к 2 n -1 возможным условным операторам?

<xsl:template match="format">
    <xsl:choose>
        <xsl:when test="@bold='1' and @italic='1'">
            <b><i><xsl:value-of-select="."/></i></b>
        </xsl:when>
        <xsl:when test="@bold='1'">
            <b><xsl:value-of-select="."/></b>
        </xsl:when>
        <xsl:when test="@italic='1'">
            <i><xsl:value-of-select="."/></i>
        </xsl:when>
    </xsl:choose>
</xsl:template>

Вы можете видеть, что существует огромная проблема, если я хочу добавить новый возможный атрибут, такой как underline="1", что приведет к появлению здесь 4 новых условий.

Редактировать: Также предположим,что я не могу использовать классы CSS и должен использовать теги HTML для стиля.

Ответы [ 4 ]

3 голосов
/ 23 марта 2012

Мой XSLT настолько ржавый, что петли не сдвигаются, но я думаю, вы можете использовать <xsl:call-template … /> для обработки одного атрибута за раз, используя один шаблон на атрибут.очевидные ошибки, но, надеюсь, это даст вам ощущение.

<xsl:template name="bold">
    <xsl:choose>
        <xsl:when test="@bold='1'">
            <b><xsl:call-template name="italics" /></b>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="italics" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="italics">
    …
</xsl:template>
1 голос
/ 23 марта 2012

Я начну с решения XSLT 2.0, а затем расскажу, как преобразовать его в XSLT 1.0.

<xsl:template match="format[@italic='1']" priority="10">
  <i><xsl:next-match/></i>
</xsl:template>

<xsl:template match="format[@bold='1']" priority="9">
  <b><xsl:next-match/></b>
</xsl:template>

<xsl:template match="format[@underline='1']" priority="8">
  <u><xsl:next-match/></u>
</xsl:template>

<xsl:template match="format" priority="7">
  <xsl:value-of select="."/>
</xsl:template>

Теперь для xsl: next-match требуется XSLT 2.0, но 1.0 имеет xsl: apply-import, который выполняет почти одинаковую работу, за исключением того, что четыре шаблонных правила теперь должны быть в отдельных модулях, каждый из которых импортирует следующий. Не удобно, но именно поэтому люди предпочитают 2.0.

1 голос
/ 23 марта 2012

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

<xsl:template name="bold">
    <xsl:choose>
        <xsl:when test="@bold='1'">
            <b><xsl:call-template name="italics" /></b>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="italics" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="italics">
        <xsl:when test="@italics='1'">
            <i><xsl:call-template name="underscore" /></i>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="underscore" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="underscore">
        <xsl:when test="@underscore='1'">
            ...
        </xsl:when>
        <xsl:otherwise>
            ...
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
0 голосов
/ 23 марта 2012

В настоящее время принятое решение создает требуемые атрибуты в фиксированном жестко заданном порядке, который не соответствует порядку атрибутов элемента format, содержащегося в исходном XML-документе .

Такое решение может быть приемлемым в случае HTML, но могут быть и другие случаи, когда желательно сохранение порядка атрибутов.

Этот ответ представляет решение, которое сохраняет порядок атрибутов . Кроме того, имена атрибутов не жестко запрограммированы в коде (может содержаться в отдельном документе), что делает код абсолютно независимым от любых изменений имен исходных атрибутов или соответствующих имен элементы, которые будут сгенерированы.

Вот простое решение XSLT 1.0, которое занимает одну таблицу стилей xslt и не использует <xsl:apply-imports>.

Это решение не зависит от порядка атрибутов или их количества и не знает их, и работает правильно, если этот порядок или количество атрибутов каким-либо образом изменяется:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <my:mapping>
   <map old="strikeout">strike</map>
 </my:mapping>

 <xsl:variable name="vMap" select="document('')/*/my:mapping/*"/>

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

 <xsl:template match="format">
   <xsl:variable name="vAttribs" select="@*"/>
   <xsl:call-template name="genAttribs">
     <xsl:with-param name="pAttribs" select="$vAttribs"/>
   </xsl:call-template>
 </xsl:template>

 <xsl:template name="genAttribs">
   <xsl:param name="pAttribs" select="/.."/>

   <xsl:choose>
     <xsl:when test="$pAttribs">
      <xsl:variable name="vMappedElemName"
        select="$vMap[@old = name($pAttribs)]"/>


      <xsl:variable name="vElemName" select=
      "concat($vMappedElemName,
              substring(name($pAttribs[not($vMappedElemName)])
                        ,1,1)
             )
      "/>

        <xsl:element name="{$vElemName}">
            <xsl:call-template name="genAttribs">
              <xsl:with-param name="pAttribs"
                   select="$pAttribs[position() > 1]"/>
            </xsl:call-template>
        </xsl:element>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="."/>
    </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к следующему документу XML :

<root>
  Pacman
    <format bold="1" italic="1"
    underscore="1"
    strikeout="1">rules</format>!
</root>

желаемый, правильный результат выдается :

<root>
  Pacman
    <b>
      <i>
         <u>
            <strike>rules</strike>
         </u>
      </i>
   </b>!
</root>

Объяснение

  1. Атрибуты обрабатываются один за другим для создания вложенных элементов. Только после обработки последнего атрибута мы генерируем текстовый узел родителя атрибута в теле сгенерированного внутреннего элемента

  2. Мы поддерживаем таблицу сопоставления - отображение attribute-name --> element-name необходимо указывать только в том случае, если требуемый перевод имени атрибута в имя элемента не является просто первой буквой имени атрибута. Если в таблице сопоставления указано имя атрибута, то мы используем строковое значение этого элемента таблицы сопоставления для генерации имени элемента.

  3. Если имя атрибута не указано в таблице сопоставления, то для имени элемента мы используем первую букву имени атрибута.

Таким образом, решение не требует каких-либо изменений, если указан какой-либо новый атрибут, а имя его соответствующего (предназначенного для создания) элемента является первой буквой имени этого атрибута .

Наконец : обратите внимание, что таблица сопоставления не обязательно должна быть частью кода XSLT (она здесь только для удобства) - в сценарии реального мира это будет отдельный документ XML ( файл) и код XSLT никогда не придется обновлять , когда необходимо добавить новое отображение attribute-name --> element-name.

...