Замена текста узла на XSLT?

#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>
  

Объяснение:

  1. Правило идентификации копирует каждый узел «как есть«.

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