#xml #xslt
#xml #xslt
Вопрос:
Я пытаюсь объединить два XML-файла с помощью XSLT1.0. Я пытаюсь объединить FileOne и Filetwo для объединения в новый XML-файл. Результирующий XML должен содержать один элемент из файла two на основе значения из тега измерения. Любая помощь была бы очень признательна
Fileone.xml
<Schedule name="NE3S">
<Item scheduleId="1" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="15" minutes="0"/>
</measPeriods>
</Item>
<Item scheduleId="2" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0002</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
</Schedule>
Filetwo.xml
<Schedule name="NE3S">
<Item scheduleId="1" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
<Item scheduleId="2" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGD0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
<Item scheduleId="3" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0002</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
</Schedule>
Мой ожидаемый результат таков
<Schedule name="NE3S">
<Item scheduleId="1" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="15" minutes="0"/>
</measPeriods>
</Item>
<Item scheduleId="2" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGD0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
<Item scheduleId="3" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0002</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0"/>
</measPeriods>
</Item>
</Schedule>
пожалуйста, обратите внимание, что ожидаемый результат
<measPeriods>
<period day="0" duration="0" hour="0" interval="15" minutes="0"/>
</measPeriods>
Я попробовал следующий код
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="fileName" select="'/opt/Filetwo.xml'" />
<xsl:param name="updates" select="document($fileName)" />
<xsl:variable name="updateMeasurement" select="$updates/Schedule/Item" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Schedule">
<xsl:copy>
<xsl:apply-templates select="Item[not(measurements/measurement = $updateMeasurement/measurements/measurement)]" />
<xsl:apply-templates select="/Schedule/Item//measurements" />
<xsl:apply-templates select="$updates/Schedule/Item/measPeriods" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
xsltproc merge.xslt /opt/Fileone.xml > /opt/FileThree.xml
мой ожидаемый результат должен содержать <measPeriods>
тег from Fileone.xml
, если значение в <measurements>/<measurement>
совпадает
отредактировал свое решение на
<?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" version="1.0"
encoding="UTF-8" indent="yes"/>
<xsl:variable name="addresses"
select="document('/opt/esymac/extras/esymacstarter/schedule/MJ.netact.xml_backup')"/>
<xsl:template match="/measSchedule">
<measSchedule name="NE3S">
<xsl:for-each select="scheduleItem">
<xsl:variable name="id" select="@scheduleId"/>
<xsl:variable name="startDate" select="@startDate"/>
<xsl:variable name="stopDate" select="@stopDate"/>
<xsl:variable name="day" select="measPeriods/period/@day"/>
<xsl:variable name="duration" select="measPeriods/period/@duration"/>
<xsl:variable name="hour" select="measPeriods/period/@hour"/>
<xsl:variable name="minutes" select="measPeriods/period/@minutes"/>
<xsl:variable name="interval"
select="$addresses/measSchedule/scheduleItem/measurements[$counterId =
@measurement]/measPeriods/@interval"/>
<Item scheduleId="{$id}" startDate="{$startDate}" stopDate="{$stopDate}">
<measurements>
<measurement><xsl:value-of select="measurements/measurement"/></measurement>
</measurements>
<measPeriods>
<period day="{$day}" duration="{$duration}" hour="{$hour}" interval="{$interval}" minutes="{$minutes}"/>
</measPeriods>
</Item>
</xsl:for-each>
</measSchedule>
</xsl:template>
</xsl:stylesheet>
теперь я правильно получаю тег элемента, но
<period day="{$day}" duration="{$duration}" hour="{$hour}" interval="{$interval}" minutes="{$minutes}"/>
все еще не подходит
Комментарии:
1. Ваша таблица стилей отражает логику, противоположную ожидаемому результату и определению проблемы. Кроме того, ключ слияния не только
measurements
.2. Как мне объединить на основе нескольких ключей?
3. Изначально я сохранил <xsl:copy> <xsl:apply-templates select=»scheduleItem[not(measurements / измерение = $updateMeasurement/ измерения / измерение)]» /> <xsl:apply-templates select=»$updateMeasurement» /> </xsl:copy> но это привело к другому результату.. Пытался поэкспериментировать с другой логикой
4. отредактировал мое решение
Ответ №1:
Судя по входным XML-файлам, он Fileone.xml
содержит подмножество записей из Filetwo.xml
. Более простым подходом будет написать XSL и применить его к Filetwo.xml
(поскольку в нем есть все записи, и нам нужно заменить только несколько) и получить доступ, Fileone.xml
используя document()
функцию.
Для совпадающего значения <measurement>
вы можете использовать <xsl:choose></xsl:choose>
для копирования period
из Fileone.xml
, а для несоответствующего значения — сохранить period
из Filetwo.xml
. Ниже находится <xsl:choose>
блок.
<xsl:choose>
<xsl:when test="$extSchedule/Item/measurements/measurement = current()/ancestor::Item/measurements/measurement">
<xsl:copy-of select="$extSchedule/Item[measurements/measurement = current()/ancestor::Item/measurements/measurement]/measPeriods/period" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="period" />
</xsl:otherwise>
</xsl:choose>
Здесь current()
используется для доступа к правильному узлу и его measurement
значению соответствующего родственного файла.
Полный XSLT выглядит следующим образом
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:strip-space elements="*" />
<xsl:param name="fileName" select="document('Fileone.xml')" />
<xsl:variable name="extSchedule" select="$fileName/Schedule" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="measPeriods">
<xsl:copy>
<xsl:choose>
<xsl:when test="$extSchedule/Item/measurements/measurement = current()/ancestor::Item/measurements/measurement">
<xsl:copy-of select="$extSchedule/Item[measurements/measurement = current()/ancestor::Item/measurements/measurement]/measPeriods/period" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="period" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Вывод ниже показывает, что period
for PGW0001
был заменен на соответствующий узел из Fileone.xml
.
<Schedule name="NE3S">
<Item scheduleId="1" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="15" minutes="0" />
</measPeriods>
</Item>
<Item scheduleId="2" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGD0001</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0" />
</measPeriods>
</Item>
<Item scheduleId="3" startDate="2013-01-01" stopDate="2037-12-31">
<measurements>
<measurement>PGW0002</measurement>
</measurements>
<measPeriods>
<period day="0" duration="0" hour="0" interval="60" minutes="0" />
</measPeriods>
</Item>
</Schedule>
Комментарии:
1. Спасибо Aniket.. Это решение сработало замечательно. Еще одно небольшое сомнение, когда я выполняю это в Linux с помощью команды xsltproc, кажется, что весь вывод находится в одной строке.. есть ли способ избежать этого?
2. <xsl:метод вывода= «xml», версия = «1.0», кодировка = «UTF-8», отступ = «yes» /> это изменило отступ на выходе.. Спасибо за ваши усилия, аникет.. Очень признателен. Я опытный разработчик Java, впервые пробующий свои силы на XSLT, и мне было трудно выучить этот нефункциональный язык..
3. Если ответ сработал для вас, не могли бы вы принять ответ. Как принять ответ