2015-01-20 6 views
-1

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

Пример для источника XML здесь:

<?xml version="1.0"?> 
<summary> 
    <incoming> 
     <delivery incDate="2013-11-08"/> 
    </incoming> 
    <outgoing> 
     <delivery outDate="20131108"/> 
    </outgoing> 
    <repairs> 
     <repair repairDate="2013-11-08 11:25:34"/> 
    </repairs> 
</summary> 

И это то, что я хочу добиться:

<?xml version="1.0"?> 
<summary> 
    <actions> 
     <action type="incoming" dateTime="2013-11-08 00:00:00"/> 
     <action type="repair" dateTime="2013-11-08 11:25:34"/> 
     <action type="outgoing" dateTime="2013-11-08 23:59:59"/> 
    </actions> 
</summary> 

Что я сделал?

  1. Унифицировать все форматы дат.
  2. Добавить время 00:00:00 для всех детей <incoming>.
  3. Добавить время 23:59:59 для всех детей <outgoing>.
  4. Добавить атрибут с именем родителя.
  5. Сортировка по дате.
+0

Я не пробовал, потому что (если честно) У меня нет подсказки, если это возможно сделать с одним преобразованием XSLT. Я сделал это с помощью C++ и pugixml ... но я хотел попробовать что-то новое, которое не полагается на 2-й язык. – fhw72

+0

Какую версию XSLT вы можете использовать? Это возможно только в том случае, если вы ограничены 1.0, но в 2.0 или более поздних версиях будет намного проще. –

+0

Я могу использовать обе версии. – fhw72

ответ

2

В XSLT 2.0 это довольно просто, как вы можете использовать два проход подход, первый генерировать требуемые выходы, а затем отсортировать их:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 

    <xsl:output method="xml" indent="yes" /> 

    <xsl:template match="/"> 
    <summary> 
     <actions> 
     <xsl:perform-sort> 
      <xsl:sort select="@dateTime" /> 
      <xsl:apply-templates select="summary/*/*" /> 
     </xsl:perform-sort> 
     </actions> 
    </summary> 
    </xsl:template> 

    <xsl:template match="incoming/delivery"> 
    <action type="incoming" dateTime="{@incDate} 00:00:00"/> 
    </xsl:template> 

    <xsl:template match="outgoing/delivery"> 
    <action type="outgoing" dateTime="{substring(@outDate, 1, 4)}-{substring(@outDate, 5, 2)}-{substring(@outDate, 7, 2)} 23:59:59"/> 
    </xsl:template> 

    <xsl:template match="repairs/repair"> 
    <action type="repair" dateTime="{@repairDate}"/> 
    </xsl:template> 
</xsl:stylesheet> 

Здесь мы генерирование выхода для каждого входа с использованием apply-templates и , затем, сортируя эти сгенерированные элементы с помощью perform-sort с использованием выражения XPath (@dateTime) относительно сгенерированного XML, а не оригинала.

Если вы ограничены 1.0, это не вариант, поскольку вы можете сортировать только на основе входных данных XML, а не сгенерированного вывода. Таким образом, мы должны придумать 1,0 выражения одного XPath, который может обрабатывать любого из трех форматов даты и произвести подходящий ключ сортировки

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 

    <xsl:output method="xml" indent="yes" /> 

    <xsl:template match="/"> 
    <summary> 
     <actions> 
     <xsl:apply-templates select="summary/*/*"> 
      <xsl:sort select=" 
      translate(
       concat(
       @incDate, @outDate, @repairDate, 
       substring('000000', 6*not(@incDate) + 1), 
       substring('235959', 6*not(@outDate) + 1) 
       ), 
       '-: ', 
       '' 
      )" /> 
     </xsl:apply-templates> 
     </actions> 
    </summary> 
    </xsl:template> 

    <!-- the other three templates are unchanged --> 

Это использует ряд трюков, прежде всего

substring('000000', 6*not(@incDate) + 1) 

которая опирается на целый ряд вещей:

  • аргумент not() трактуется как логическое значение.
  • когда вы лечите узел установлен как логическое значение, пустое множество является ложным и непустым множество истинно
  • литья булева номера дает 1 для истинно и 0 для ложного

поэтому эффект для возврата строки 000000, если целевой узел имеет атрибут incDate, а пустую строку - нет. Окончательный concat создает строку, которая будет выглядеть как YYYY-MM-DD000000 для incDate, YYYYMMDD235959 для outDate и YYYY-MM-DD HH:mm:ss для repairDate, то translate обрезает все пробелы, дефис и колонов, чтобы привести эти три в общий формат, который можно сравнить лексически.

+0

Спасибо. Теперь я «только» должен это полностью понять! :-) Я уверен, что я ... просто займет некоторое время. Просто из любопытства: Было ли решение XSLT 1.0 намного сложнее и учить XSLT 1.0 по-прежнему рекомендуется? – fhw72

+0

@ fhw72 это возможно в 1.0, но вам нужно сортировать входные узлы, а не сгенерированный вывод, а язык XPath гораздо более ограничен, поэтому вам нужно использовать несколько идиоматических трюков, чтобы иметь возможность использовать полезный ключ сортировки. Если у вас есть возможность использовать 2.0, то возьмите его, язык 2.0 (как XPath, так и XSLT) намного приятнее 1.0, но, к сожалению, многие XSLT-процессоры застряли в версии 1.0. –

+0

Еще раз спасибо. Я сделаю, как вы предлагаете, и придерживайтесь 2.0! Интересно видеть эти два разных подхода, и я очень хочу узнать (или хотя бы понять) обе возможности! – fhw72

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