Копирование XML с удалением некоторых узлов с помощью XSLT

Я знаю, что на него уже был дан ответ здесь XSL: как скопировать дерево, но удалить некоторые узлы?, но у меня есть более сложный XML-файл, и он работал не очень хорошо.

Весь этот XML и XSLT для меня в новинку, и мой босс поручил мне преобразовать XML (файл OVF из VMWare) в другой и удалить одни узлы, добавить другие и обновить информацию. У меня есть оба XML-файла, и моя задача - разработать XSLT, который их преобразует.

Вот исходный XML:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Built using IBM Image Construction and Composition Tool, version: 1.2.0.1-20121129-1310-255 on: Oct 18, 2013 12:14:22 -->
<Envelope
    xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
    xmlns:cloudburst="http://www.ibm.com/websphere/rainmaker/2009/3" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
    xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" cloudburst:name="POSTGRES-9.2.4-RHEL-64.X64.xxx.xxx"
    cloudburst:version="1.0.0" cloudburst:build="sample" cloudburst:serviceLevel="0"
    cloudburst:description="BASEIMAGE FOR POSTGRESQL 9.2.4" cloudburst:symbolicName="POSTGRES-9.2.4-RHEL-64.X64.xxx.xxx">
  <References>
    <File ovf:href="en-US-bundle.msg" ovf:id="en-US-bundle.msg" ovf:size="18526"/>
    <File ovf:href="de-DE-bundle.msg" ovf:id="de-DE-bundle.msg" ovf:size="20687"/>
    <File ovf:href="es-ES-bundle.msg" ovf:id="es-ES-bundle.msg" ovf:size="20364"/>
    <File ovf:href="fr-FR-bundle.msg" ovf:id="fr-FR-bundle.msg" ovf:size="20534"/>
    <File ovf:href="it-IT-bundle.msg" ovf:id="it-IT-bundle.msg" ovf:size="20138"/>
    <File ovf:href="ja-JP-bundle.msg" ovf:id="ja-JP-bundle.msg" ovf:size="23116"/>
    <File ovf:href="ko-KR-bundle.msg" ovf:id="ko-KR-bundle.msg" ovf:size="19114"/>
    <File ovf:href="pt-BR-bundle.msg" ovf:id="pt-BR-bundle.msg" ovf:size="20204"/>
    <File ovf:href="zh-CN-bundle.msg" ovf:id="zh-CN-bundle.msg" ovf:size="16875"/>
    <File ovf:href="zh-TW-bundle.msg" ovf:id="zh-TW-bundle.msg" ovf:size="18395"/>
    <File ovf:href="Automation.topology" ovf:id="Automation.topology" ovf:size="196121"/>
    <File ovf:href="Semantic.topology" ovf:id="Semantic.topology" ovf:size="34496"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis.vmdk"
        ovf:size="3129636864"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_1.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_1.vmdk"
        ovf:size="470930944"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_2.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_2.vmdk"
        ovf:size="597504"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_3.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_3.vmdk"
        ovf:size="8147968"/>
    <File ovf:href="default1382090373335.xml" ovf:id="default1382090373335.xml"
        ovf:size="17914" cloudburst:part2Definition="true"/>
    <File ovf:href="default1382090373335C.xml" ovf:id="default1382090373335C.xml"
        ovf:size="15854" cloudburst:part2Definition="true"/>
  </References>
</Envelope>

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

Это должно выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Built using IBM Image Construction and Composition Tool, version: 1.2.0.1-20121129-1310-255 on: Oct 18, 2013 12:14:22 -->
<Envelope
    xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
    xmlns:cloudburst="http://www.ibm.com/websphere/rainmaker/2009/3" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
    xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" cloudburst:name="POSTGRES-9.2.4-RHEL-64.X64.xxx.xxx"
    cloudburst:version="1.0.0" cloudburst:build="sample" cloudburst:serviceLevel="0"
    cloudburst:description="BASEIMAGE FOR POSTGRESQL 9.2.4" cloudburst:symbolicName="POSTGRES-9.2.4-RHEL-64.X64.xxx.xxx">
  <References>
    <File ovf:href="en-US-bundle.msg" ovf:id="en-US-bundle.msg" ovf:size="18526"/>
    <File ovf:href="Automation.topology" ovf:id="Automation.topology" ovf:size="196121"/>
    <File ovf:href="Semantic.topology" ovf:id="Semantic.topology" ovf:size="34496"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis.vmdk"
        ovf:size="3129636864"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_1.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_1.vmdk"
        ovf:size="470930944"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_2.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_2.vmdk"
        ovf:size="597504"/>
    <File ovf:href="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_3.vmdk" ovf:id="RedHat6-4-64-Base-PRB-HARDENEDv1-1-bis_3.vmdk"
        ovf:size="8147968"/>
    <File ovf:href="default1382090373335.xml" ovf:id="default1382090373335.xml"
        ovf:size="17914" cloudburst:part2Definition="true"/>
    <File ovf:href="default1382090373335C.xml" ovf:id="default1382090373335C.xml"
        ovf:size="15854" cloudburst:part2Definition="true"/>
  </References>
</Envelope>

Как видите, мне нужно выбрать все File узлы, содержащие «пакет», и избавиться от них, кроме первого (который содержит en-US). Написанный мною xPath для их выбора:

/Envelope/References/File[contains(@ovf:href, 'bundle')][position()>1]

(У меня были проблемы с этим, потому что - я думаю - все пространства имен, но я попробовал это в Altova XMLspy, и он работал безупречно)

Поскольку я никогда не программировал с помощью XSL, он немного отличается от всего, что я знаю (в основном C, Java, PHP, VB.net ...), но я знаю HTML, поэтому базовая структура мне известна.

Итак, у меня вопрос: как будет выглядеть XSL при копировании всего XML, но игнорировании этого подмножества File узлов?

Это не сработало, что я скопировал из того ответа SO, который я связал ранее

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" >

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/Envelope/References/File[contains(@href, 'bundle')][position()>1]"/> <!-- this empty template will remove them -->
</xsl:stylesheet>

Думаю, не имеет значения, использую ли я XSL v1 или v2, на самом деле я не знаю различий между ними: D

Спасибо


person fernandopcg    schedule 22.10.2013    source источник
comment
Вы хотите удалить все узлы File, содержащие пакет, или все, кроме первого? (Пример вывода, который вам нужен, по-прежнему содержит en-US-bundle.msg File). Кроме того, в вашем примере показано изменение атрибутов cloudburst:name и cloudburst:symbolicName в Envelope. Вас интересуют эти изменения?   -  person Ben L    schedule 22.10.2013
comment
Ой, извините, я только что отредактировал и исправил те ошибки. Да, я имел в виду удалить все File узлы, кроме первого. Отличия в коде возникли по ошибке, этого не должно быть, но сейчас все в порядке.   -  person fernandopcg    schedule 22.10.2013


Ответы (1)


Это из-за пространств имен. Во входном XML вы определили пространство имен по умолчанию с xmlns="http://schemas.dmtf.org/ovf/envelope/1 и пространство имен ovf xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1. Элементы File принадлежат пространству имен по умолчанию, а атрибуты @href принадлежат пространству имен ovf. Эти пространства имен совпадают.

Вам необходимо определить то же пространство имен в своем XSLT, а затем сопоставить элементы и атрибуты, используя это пространство имен. (Обратите внимание, что вы можете называть пространство имен как угодно, если его значение совпадает с соответствующим значением во входных данных. Ниже я назвал его ns.)

Следующая таблица стилей удалит все, кроме первого File узла, который содержит "bundle".

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:ns="http://schemas.dmtf.org/ovf/envelope/1">
  <xsl:output method="xml" indent="yes" />
  <xsl:strip-space elements="*"/>

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

  <!-- this empty template will remove them -->
  <xsl:template match="ns:Envelope/ns:References/ns:File[contains(@ns:href, 'bundle')][position()>1]"/>
</xsl:stylesheet>
person Ben L    schedule 22.10.2013
comment
Спасибо, ваш ответ мне очень помог :) Теперь мне нужно изменить остальную часть XML, но теперь это будет проще, когда у меня есть рабочий пример. Я не уверен в одном: почему некоторые окончания строк и отступы перепутаны, но сначала я сосредоточусь на XSL. Еще раз спасибо! - person fernandopcg; 23.10.2013