2013-10-11 2 views
3

Я хочу изменить порядок некоторых элементов XML. XML является сложным и генерируется отдельным процессом - я не платил за его изменение, поэтому я надеялся использовать XSLT для исправления порядка элементов.Изменение порядка элементов XML при сохранении структурной иерархии и атрибутов

Я не эксперт XSLT (!), Поэтому я искал некоторые фрагменты и нашел что-то, что с незначительными изменениями в соответствии с моим случаем, почти работает. В лучшем варианте, который у меня есть, в настоящее время выводятся элементы в правильном порядке, но удаляются все атрибуты.

Я создал простой xml и соответствующий xsl с соответствующими функциями моей проблемы.

Здесь (фиктивный) Пример XML:

<?xml version="1.0" encoding="UTF-8"?> 
<Companies xmlns="company:fruit:ns" Version="1.0"> 
    <Description>Some example companies and fruit shipments</Description> 
    <Company CompanyId="Acme"> 
    <Description>Some example shipments</Description> 
    <Shipment Id="ABC"> 
     <Description>Some apples</Description> 
     <Fruit> 
     <Apples>10</Apples> 
     </Fruit> 
    </Shipment> 
    <Shipment Id="DEF"> 
     <Description>Some oranges and pears</Description> 
     <Fruit> 
     <Oranges>20</Oranges> 
     <Pears>20</Pears> 
     </Fruit> 
    </Shipment> 
    <Shipment Id="JKL"> 
     <Description>Empty</Description> 
     <Fruit/> 
    </Shipment> 
    <Fruit/> 
    </Company> 
    <Fruit/> 
</Companies> 

Проблема заключается в том, что должно быть элемент фирмы-фрукты после компании-Описание элемента (вместо этого следует всем элементам отгрузки) и должно быть элементом «Компании-фрукты», который следует за элементом «Описание компаний» (вместо этого он соответствует всем элементам Компании-компании). Я использовал следующее преобразование XSL, чтобы исправить порядок элементов:

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xpath-default-namespace="company:fruit:ns"> 
    <!-- See http://xsltbyexample.blogspot.com/2008/02/re-arrange-order-of-elements-in-xml.html --> 
    <xsl:output omit-xml-declaration="no" indent="yes" method="xml" encoding="utf-8"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="*"> 
    <xsl:apply-templates select="self::*" mode="copy"/> 
    </xsl:template> 
    <xsl:template match="Company/Description"> 
    <xsl:message>Matched Company Description</xsl:message> 
    <xsl:apply-templates select="self::*" mode="copy"/> 
    <xsl:apply-templates select="../Fruit" mode="copy"/> 
    </xsl:template> 
    <xsl:template match="Companies/Description"> 
    <xsl:message>Matched Companies Description</xsl:message> 
    <xsl:apply-templates select="self::*" mode="copy"/> 
    <xsl:apply-templates select="../Fruit" mode="copy"/> 
    </xsl:template> 
    <xsl:template match="Company/Fruit"/> 
    <xsl:template match="Companies/Fruit"/> 
    <xsl:template match="*" mode="copy"> 
    <xsl:copy> 
     <xsl:apply-templates/> 
    </xsl:copy> 
    </xsl:template> 
    <xsl:template match="text()"> 
    <xsl:value-of select="."/> 
    </xsl:template> 
</xsl:stylesheet> 

Результирующий XML имеет право упорядоченность, но большинство атрибутов были раздеты:

<?xml version="1.0" encoding="utf-8"?> 
<Companies xmlns="company:fruit:ns"> 
    <Description>Some example companies and fruit shipments</Description> 
    <Fruit/> 
    <Company> 
     <Description>Some example shipments</Description> 
     <Fruit/> 
     <Shipment> 
     <Description>Some apples</Description> 
     <Fruit> 
      <Apples>10</Apples> 
     </Fruit> 
     </Shipment> 
     <Shipment> 
     <Description>Some oranges and pears</Description> 
     <Fruit> 
      <Apples>20</Apples> 
      <Pears>20</Pears> 
     </Fruit> 
     </Shipment> 
     <Shipment> 
     <Description>Empty</Description> 
     <Fruit/> 
     </Shipment> 
    </Company> 
</Companies> 

Я хотел бы получить какие-либо рекомендации от Эксперты XSLT!

+0

Когда вы копируете свой элемент с атрибутами, просто скопируйте их с помощью '. Это копирует все атрибуты из текущего узла в выходную позицию. Либо вы определяете другой шаблон, как тот, который соответствует 'text()', или вы помещаете эту команду в свой блок '' в шаблон режима копирования. – Matthias

+0

@ Matthias - спасибо за ваш быстрый и полезный ответ. Я попытался внести изменения, которые вы предлагаете, пробовав их в нескольких местах в xsl, но в целом он не имел желаемого эффекта. Возможно, я неправильно понял ваш комментарий .... – rugplots

ответ

4

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

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

Затем вы соответствуете этому шаблону.

<!-- throw away <Fruit> elements, initially - they are handled separately --> 
<xsl:template match="Company/Fruit | Companies/Fruit" /> 

<!-- re-build <Company> and <Companies> in the correct order --> 
<xsl:template match="Company | Companies"> 
    <xsl:copy> 
    <xsl:copy-of select="@*" /> 
    <xsl:copy-of select="Fruit" /> 
    <xsl:apply-templates select="node()" /> 
    </xsl:copy> 
</xsl:template> 

А потом все готово.

+0

Спасибо @Tomalak за ваш очень четкий и полезный ответ. К сожалению, пока это не работает.Процессор xslt жалуется на ошибку времени выполнения в преобразовании идентичности - заявив, что «Узел атрибута (Версия) не может быть создан после дочернего элемента содержащего элемента». – rugplots

+0

Ничего себе, какой процессор XSLT вы используете? o_O – Tomalak

+0

Дальнейшее - возможно, незначительное - проблема может заключаться в том, что элемент 'Fruit' также появляется в каждой« Отгрузке »и, возможно, не должен быть выброшен. Вот почему мой оригинальный xsl был более конкретным о том, какие элементы «Фрукты» отбрасываются. – rugplots

3

Решение @Tomalak показывает, как изменить порядок элементов таким образом, что, например, элементы <Fruit> под номером <Company> больше не соответствуют элементам <Shipment>. Однако решение Томалака ставит элементы <Fruit> перед элементами <Description>. Заказ должен быть <Description>, <Fruit>, <Shipment>,.... (!)

Поэтому для полноты картины, и доказать, что я узнал от @ решений Томалака, в соответствующих части полной версии XSL приведены здесь:

<!-- throw away <Fruit> and <Description> elements, initially - they are handled separately --> 
<xsl:template match="Company/Fruit | Companies/Fruit | Companies/Description | Company/Description"/> 

<!-- re-build <Company> and <Companies> in the correct order --> 
<xsl:template match="Company | Companies"> 
<xsl:copy> 
    <xsl:copy-of select="@*"/> 
    <xsl:copy-of select="Description"/> 
    <xsl:copy-of select="Fruit"/> 
    <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
</xsl:template> 

Еще раз спасибо @Tomalak для выполнения тяжелой работы ...

+0

Приятно, чтобы вы также опубликовали свои результаты/собственное решение. Так держать! :) – Tomalak

Смежные вопросы