XSL с несколькими шаблонами

Я пытаюсь создать XSL, который преобразует XML, используя следующие шаги:

  1. Определите, имеет ли элемент под корневыми «свойствами» имя, начинающееся с «foo». Если он не начинается с «foo», удалите элемент.
  2. Создайте новые элементы с именем «entry» с атрибутом «key», равным имени элементов, начинающихся с «foo».

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

XML-документ

<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <oof>AAA</oof>
    <bar>BBB</bar>
    <foobar>CCC</foobar>
    <barfoo>DDD</barfoo>
    <foofoofoobar>EEE</foofoofoobar>
</properties>

Мой XSLT, который только изменяет элемент на запись

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

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

  <xsl:template match="properties/*">
     <entry>
         <xsl:attribute name="key" select="name()"/>
         <xsl:apply-templates select="@* | node()"/>
     </entry>
  </xsl:template>

Когда я пытаюсь использовать другой шаблон <xsl:template match="properties[not(contains(name(), 'foo'))]"/>, он не выполняется после шаблона элемента "entry". Если я делаю шаблон перед шаблоном записи, шаблон записи не выполняется.

Желаемый результат

<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <entry key="foobar">CCC</entry>
    <entry key="foofoofoobar">EEE</entry>
</properties>

Ваша помощь очень ценится.

ИЗМЕНИТЬ

Мне удалось создать <xsl:template match="entry[not(contains(@*, 'foo'))]"/>, который удалит элементы ввода, не содержащие foo, что не совсем то, что я хочу, но это еще один шаг в правильном направлении. Однако, если я помещу все это в один XSLT-документ, будет работать только первый шаблон.


person Jayson    schedule 15.08.2018    source источник


Ответы (1)


Я предполагаю, что другой шаблон выглядит так для целей вопроса

<xsl:template match="properties[not(contains(name(), 'foo'))]" /> 

Это соответствует элементу properties, и, поскольку имя «свойства» не содержит текста «foo», условие истинно, и поэтому обработка фактически останавливается здесь (т. е. properties не копируется, и ни один из дочерних узлов entry не получает совпало).

Вы, вероятно, захотите сделать это (я поменял местами с contains на starts-with, так как это соответствует вашим требованиям)

<xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" /> 

Но... Здесь есть проблема с приоритетом шаблона.

Элемент properties/bar (например) будет соответствовать обоим вашим шаблонам, поэтому XSLT должен вызывать Conflict Решение для шаблонных правил (если описание кажется слишком сложным, см. http://www.lenzconsulting.com/how-xslt-works/#priority). Это означает, что оба ваших шаблона имеют одинаковый приоритет; 0,5. Что обычно происходит в этом случае, так это то, что процессор будет использовать последний совпадающий шаблон в XSLT, хотя некоторые процессоры могут сигнализировать об ошибке.

Чтобы решить эту проблему, вы можете либо дать шаблону приоритет

<xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" priority="0.6" />

Или вы можете добавить условие к другому шаблону

<xsl:template match="properties/*[starts-with(name(), 'foo')]">

Или вы можете объединить два шаблона в один...

<xsl:template match="properties/*">
  <xsl:if test="starts-with(name(), 'foo')">
    <entry key="{name()}">
      <xsl:apply-templates select="@* | node()"/>
    </entry>
  </xsl:if>
</xsl:template>

Попробуйте этот XSLT (который использует метод приоритета)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

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

  <xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" priority="0.6" />

  <xsl:template match="properties/*">
     <entry key="{name()}">
         <xsl:apply-templates select="@* | node()"/>
     </entry>
  </xsl:template>
</xsl:stylesheet>

В качестве бонуса в этом XSLT также используются шаблоны значений атрибутов. чтобы создать атрибут key для элементов entry.

person Tim C    schedule 15.08.2018
comment
Это фантастика! Прекрасно работает. Одна проблема заключается в том, что когда он удаляет узлы, которые не начинаются с «foo», между узлами остается пробел. Как убрать пробел? - person Jayson; 15.08.2018
comment
Удалите пустое пространство, используя xsl:strip-space. См. xsltfiddle.liberty-development.net/jyH9rMB. - person Tim C; 15.08.2018
comment
Идеально! Спасибо за вашу помощь в моем XSL. Это выглядит великолепно! - person Jayson; 15.08.2018