#xml #xslt
#xml #xslt
Вопрос:
Ниже приведен мой XML-файл, который используется для хранения данных —
<Locations>
<location>
<place>Newyork</place>
<dt>01-Dec-2011</dt>
</location>
<location>
<place>Berlin</place>
<dt>02-Dec-2011</dt>
</location>
<location>
<place>Tokyo</place>
<dt>04-Dec-2011</dt>
</location>
</Location>
Чего я хочу добиться, так это —
Я хочу заменить <dt>
значение даты тегов, если посещение запланировано повторно. Например- если изменена дата посещения Берлина, сохраненная в <dt>
тегах, то как отредактировать / заменить то же самое в файле XML с использованием XSLT ..? Заранее спасибо — Джон
Комментарии:
1. Хороший вопрос, 1. Переопределение правила идентификации и передача обновлений в качестве внешнего параметра для преобразования — это короткое и наиболее эффективное решение этой проблемы.
2. @Dimitre- Правильно сказал и большое спасибо 🙂
Ответ №1:
Это преобразование показывает, как использовать глобальный параметр (смоделированный здесь с помощью встроенного элемента) для указания (возможно, нескольких) обновлений:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:updates>
<update place="Berlin" dt="11-Dec-2011"/>
</my:updates>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"location
[place = document('')/*/my:updates/update/@place]
/dt/text()
">
<xsl:value-of select=
"document('')/*/my:updates/update
[@place = current()/../../place]
/@dt
"/>
</xsl:template>
</xsl:stylesheet>
При применении к предоставленному XML-документу (исправлено, чтобы сделать его правильно сформированным):
<Locations>
<location>
<place>Newyork</place>
<dt>01-Dec-2011</dt>
</location>
<location>
<place>Berlin</place>
<dt>02-Dec-2011</dt>
</location>
<location>
<place>Tokyo</place>
<dt>04-Dec-2011</dt>
</location>
</Locations>
получен желаемый, правильный результат:
<Locations>
<location>
<place>Newyork</place>
<dt>01-Dec-2011</dt>
</location>
<location>
<place>Berlin</place>
<dt>11-Dec-2011</dt>
</location>
<location>
<place>Tokyo</place>
<dt>04-Dec-2011</dt>
</location>
</Locations>
Объяснение:
-
Правило идентификации копирует каждый узел «как есть«.
-
Существует только один переопределяющий шаблон — соответствующий дочернему текстовому узлу любого, строковое значение
dt
которогоplace
имеет соответствующийmy:updates/update
элемент. В этом шаблоне мы выводим значениеdt
атрибута этого соответствующегоmy:updates/update
элемента.
Обратите внимание: при преобразовании реального мира встроенный my:updates
элемент будет лучше заменить внешним глобальным параметром. Прочитайте документацию вашего процессора XSLT о том, как передать внешний параметр преобразованию — это зависит от реализации.
ОБНОВЛЕНИЕ: Поскольку OP затруднился преобразовать это решение в одно, используя глобальное, переданное извне xsl:param
, вот это преобразованное решение:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pUpdates">
<update place="Berlin" dt="11-Dec-2011"/>
</xsl:param>
<xsl:variable name="vUpdates" select=
"ext:node-set($pUpdates)/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dt/text()">
<xsl:choose>
<xsl:when test="../../place=$vUpdates/@place">
<xsl:value-of select=
"$vUpdates[@place = current()/../../place]/@dt"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к тому же документу XML (выше), получается тот же правильный и желаемый результат:
<Locations>
<location>
<place>Newyork</place>
<dt>01-Dec-2011</dt>
</location>
<location>
<place>Berlin</place>
<dt>11-Dec-2011</dt>
</location>
<location>
<place>Tokyo</place>
<dt>04-Dec-2011</dt>
</location>
</Locations>
Обратите внимание: в этом решении xsl:param
значение по-прежнему жестко задано, и это единственная причина, по которой мы используем ext:node-set()
функцию расширения. Если параметр действительно передается извне, то это преобразование из RTF в обычное дерево не требуется, и на параметр следует ссылаться напрямую.
Кроме того, в XSLT 1.0 мы должны сопоставлять более неточно и использовать сравнения ( xsl:choose
) внутри тела шаблона. Это так, потому что в XSLT 1.0 не разрешено ссылаться на переменные / параметры внутри шаблона сопоставления.
В XSLT 2.0 это ограничение было устранено, поэтому мы можем просто выполнить гораздо более простое преобразование:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pUpdates">
<update place="Berlin" dt="11-Dec-2011"/>
</xsl:param>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"location[place=$pUpdates/*/@place]/dt/text()">
<xsl:value-of select=
"$pUpdates/*[@place = current()/../../place]/@dt"/>
</xsl:template>
</xsl:stylesheet>
Комментарии:
1. Большое спасибо.. для очень четкого и сжатого объяснения. Снимаю шляпу перед вами 🙂
2. Итак, при передаче параметров мне придется использовать следующий тег — /<xsl:param name=»paraPlace» select=»»/>` и
<xsl:param name="paraDate" select=""/>
и придется использовать эти параметры в<my:update...>
теге. Верно ..?3. Я получаю следующую ошибку — ——————— Конфигурация: ‘<Поумолчанию>——————— ОШИБКА: ‘Преждевременное завершение файла.’ ОШИБКА: ‘com.sun.org.apache.xml.internal.utils. Исключение WrappedRuntimeException: преждевременный конец файла.’ Исключение в потоке «main» javax.xml.transform. Исключение TransformerException:’ при попытке обновить тот же XML-файл. Но когда преобразованный XML-файл отличается, ошибок НЕТ!!
4. @John: Я обновил ответ соответствующим преобразованием, которое будет использоваться с глобальным
<xsl:param>
. Я также предоставляю более простое и короткое решение XSLT 2.0 на случай, если вы сможете использовать XSLT 2.0. Надеюсь, это ответит на ваши новые вопросы.
Ответ №2:
Шаблон идентификации скопирует документ:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
Затем вы можете создавать другие шаблоны только для тех частей, которые вы хотите изменить.
Пример (непроверенный):
<xsl:template match="//location/dt[preceding-sibling::place='Berlin']">
<dt>Your date</dt>
</xsl:template>
Ответ №3:
Реальный вопрос в том, как вы собираетесь проверять, не запланировано ли повторное посещение? Насколько я понимаю, у вас есть три варианта:
- Сохраните переназначенные даты с местами во вторичном XML и используйте функцию XPath
document
для чтения из него; - создайте таблицу стилей XSLT программно с уже внесенными исправлениями или
- используйте функцию расширения XSLT или элемент расширения (функции, вероятно, будет достаточно) для выполнения проверок на каком-либо другом языке. Например, Java.
ОТРЕДАКТИРУЙТЕ — или следуйте замечательному предложению Krab: используйте параметры XSLT таким образом, чтобы вы могли передавать данные в статическую таблицу стилей.
Комментарии:
1. Эй, как насчет параметров таблицы стилей — просто передайте ему место и новую дату.
2. @Krab, конечно… Я действительно должен помнить, что вы можете передавать параметры. Кто знает, какую сумасшедшую таблицу стилей я генерировал, чего можно было бы избежать. Собираюсь отредактировать это.