2010-10-12 5 views
0

Im работает с XSLT1.0 (процессор не может обрабатывать 2.0) и имеют проблемы, пытаясь группе вывод структуры XML:XSL группировка xml по элементу

< строки >
< порядка >
< текст > некоторый порядок текст 1
</текст >
</заказ>
</row>

<
< оплаты >
< текст > некоторые платежа текст 1
</текст >
</оплаты>
</строка>

< строки >
< заказ >
< текст > некоторый порядок текст 2
</текст >
</заказ>
</строка>

< строки >
< контакт >
< текст > некоторые контактные данные 1
</текст >
</контакт>
</строка>

< строки >
< контакт >
< текст > некоторые контактные данные 2
</текст >
</контакт>
</строка>

Сегодня мы выбираем все строки и называем применить шаблон для каждого (каждого типа имеет свой собственный шаблон, который выписывает свое тело), ​​что создает выход, как:

ордена: некоторый порядок TEXT1
заказа: некоторый порядок text2
Оплата: некоторые оплаты text1
Контакт: некоторые контакты details1
Контакт: некоторые контакты details2

Но то, что я хотел бы, чтобы (в XSLT 1.0) в группу выход, так что:

Заказать

  1. некоторый порядок text1
  2. некоторый порядок text2

Оплата

  1. некоторые оплаты TEXT1

Контакт

  1. некоторые контакты details1
  2. некоторые контакты details2

Очевидно, что существует много других типов элементов, чем заказ, оплата и контакт вовлеченного здесь поэтому выбор явных имен элементов не является решением проблемы.

EDIT

Ty, некоторые большие ответы, как бы Muenchian группировка изменения решение, если я имел структуру сказать

<customers> 
    <person> 
    <row>....</row> (row is same as above) 
    <row>....</row> 
    </person> 
    <person> 
    <row>....</row> 
    <row>....</row> 
    <row>....</row> 
    </person> 

Затем ключ:

<xsl:key name="type" match="row/*" use="local-name()"/> 

выбрать бы все ряды по всем лицам, которых я не хотел. Спасибо за отличные ответы.

+0

возможно дубликат [как применить группу, на XSLT элементов] (http://stackoverflow.com/questions/2146648/how-to-apply-group-by-on-xslt-elements) – annakata

+0

Duped много, много, * много *, раз больше. – annakata

+0

@ user408346: Пожалуйста, задайте новый вопрос. Не добавляйте к этому вопросы новые вопросы. – 2010-10-13 12:40:44

ответ

1

Try:

<xsl:template match="(parent element-whatever contains the 'row' elements)"> 
    <xsl:apply-templates> 
    <xsl:sort select="name(*)" /> 
    </xsl:apply-templates> 
</xsl:template> 

Это сортирует элементы строки от имени первого ребенка.

Этот шаблон добавляет в заголовок:

<xsl:template match="row"> 
    <xsl:copy> 
    <xsl:if test="not(preceding-sibling::*[name(*) = name(current()/*)])"> 
     <!-- Output header here --> 
     <xsl:value-of select="name(*)" /> 
    </xsl:if> 
    <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
</xsl:template> 

Тест в основном говорит: "Выход в этом случае нет никаких предыдущих братьев с тем же именем.

+0

За исключением этого не будет распечатываться заголовок раздела («Заказ»). – LarsH

+0

P.S. Но мне нравится идея использовать сортировку как простой способ группировать вещи. +1 – LarsH

+0

Правда, полагаю; Я интерпретировал вывод как описание, а не точный текст. Я редактировал способ добавления в заголовок. EDIT: Хех, только что заметил, это более или менее то, что ваш ответ делает :) Я просто не вдавался в так подробно, форматируя вывод. – Flynn1179

4

Выполнение этой задачи в XSLT 1.0 вам необходимо использовать Muenchian grouping, но проще (на мой взгляд) решить с xsl:for-each-group в XSLT 2.0.

Следующая таблица стилей XSLT 1.0 будет делать то, что вы просите. Ключ должен использовать ключ (doh!), Который позволит вам группировать локальные имена узлов.

Вход:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <row> 
    <order> 
     <text>some order text 1</text> 
    </order> 
    </row> 

    <row> 
    <payment> 
     <text>some payment text 1</text> 
    </payment> 
    </row> 

    <row> 
    <order> 
     <text>some order text 2</text> 
    </order> 
    </row> 

    <row> 
    <contact> 
     <text>some contact details 1</text> 
    </contact> 
    </row> 

    <row> 
    <contact> 
     <text>some contact details 2</text> 
    </contact> 
    </row> 
</root> 

XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="text"/> 
    <xsl:key name="type" match="row/*" use="local-name()"/> 

    <xsl:template match="root"> 
    <xsl:for-each select="row/*[ 
     generate-id() = generate-id(key('type', local-name())[1])]"> 
     <xsl:value-of select="local-name()"/> 
     <xsl:text>&#x0a;</xsl:text> 
     <xsl:for-each select="key('type', local-name())"> 
     <xsl:value-of select="concat(' ', position(), '. ')"/> 
     <xsl:apply-templates select="text"/> 
     <xsl:text>&#x0a;</xsl:text> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 

</xsl:stylesheet> 

Выход:

order 
    1. some order text 1 
    2. some order text 2 
payment 
    1. some payment text 1 
contact 
    1. some contact details 1 
    2. some contact details 2 
+0

+1 Cheers для Muenchian. Это, скорее всего, будет лучше, чем мой ответ, для больших наборов данных. – LarsH

+0

+1 Хороший ответ, помимо шаблона «кирпича» ... – 2010-10-12 13:20:35

+0

Ty, некоторые отличные ответы, как изменилось бы решение группы Muenchian, если бы у меня была структура сказанных клиентов, содержащих лиц, имеющих строки (например, выше), затем ключ определенный выше, будет перебирать все строки по всем лицам? Я бы хотел, чтобы он перебирал все строки для каждого элемента person. – user408346

1

Опираясь на @ ответ Флинна ...

Если у вас есть этот шаблон для родителей (не показан в вашем образце):

<xsl:template match="row-parent"> 
    <xsl:apply-templates select="row"> 
    <xsl:sort select="name(*[1])" /> 
    </xsl:apply-templates> 
</xsl:template> 

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

Тогда для того, чтобы добавить заголовки разделов, шаблон для обработки детей использует условное, чтобы увидеть, если это первая строка его секции:

<xsl:template match="row"> 
    <xsl:variable name="childName" select="name(*[1])"/> 
    <!-- if this is the first row with an element child of this name --> 
    <xsl:if test="not(preceding-sibling::row[name(*[1]) = $childName])"> 
     <xsl:value-of select="concat('&#10;', 
     translate(substring($childName, 1, 1), $lower, $upper), 
     substring($childName, 2), '&#10;&#10;')"/> 
    </xsl:if> 

Затем выводят данные для каждой строки, что группа, с форматированием вы хотите:

<xsl:number level="any" count="row[name(*[1]) = $childName]" format=" 1. " 
     from="row-parent"/> 
    <xsl:value-of select="normalize-space(*[1])"/> 
    <xsl:text>&#10;</xsl:text> 
</xsl:template> 

Как обычно, $ нижний и верхний $ определяются в верхней части шаблона (или таблицы стилей), как

<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/> 
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> 

И сделать таблицу стилей использовать «текст» метод вывода:

<xsl:output method="text"/> 

Выход выше таблицы стилей на вашем входе (в пределах <row-parent> обертке) является:

Contact 

1. some contact details 1 
2. some contact details 2 

Order 

1. some order text 1 
2. some order text 2 

Payment 

1. some payment text 1 

В качестве альтернативы, и многое другое вы можете использовать Muenchian grouping: сначала группировать строки по имени дочернего элемента, затем (выводить заголовок для каждой группы и) обрабатывать все строки внутри группы.

1

Кроме хороших ответов с методом группировки, таблицы стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:variable name="vSort" select="'|order|payment|contact|'"/> 
    <xsl:template match="/"> 
     <xsl:apply-templates select="*/row"> 
      <xsl:sort select="string-length(
           substring-before($vSort, 
                concat('|', 
                 name(), 
                 '|')))"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="row/*"> 
     <xsl:variable name="vName" select="name()"/> 
     <xsl:variable name="vNumber"> 
      <xsl:number level="any" count="*[name()=$vName]" from="/"/> 
     </xsl:variable> 
     <xsl:if test="$vNumber = 1"> 
      <xsl:value-of select="concat(translate(substring(name(),1,1), 
                 'opc', 
                 'OPC'), 
              substring(name(),2), 
              '&#xA;')"/> 
     </xsl:if> 
     <xsl:value-of select="concat($vNumber,'. ',text,'&#xA;')"/> 
    </xsl:template> 
</xsl:stylesheet> 

выход (с хорошо сформированной вход):

Order 
1. some order text 1 
Payment 
1. some payment text 1 
2. some order text 2 
Contact 
1. some contact details 1 
2. some contact details 2 
+0

К сожалению, ОП уже сказал, что существует много других типов элементов, чем order/payment/contact, поэтому выбор на 'xsl: sort', скорее всего, будет неадекватным. Почему 1,2,4? Разумеется, 1,2,3 будут делать в этом случае? – Flynn1179

+0

@ Flynn1179: таких общих решений нет. Это преобразование дает желаемый результат. Ваш порядок сортировки ('name (*)') не выполняется: 'contact' <' order' <'payment'. Сила последовательности 2 - это привычка битовой маски. См. Мое редактирование для более многоразового выражения сортировки. – 2010-10-12 16:35:47

+0

Да, если вам нужен определенный порядок сортировки, тогда общее решение не будет работать. Я работал в предположении, что порядок групп в выпуске не важен (или в алфавитном порядке); если это так, то я соглашаюсь, что необходимо явно объявить его каким-то образом, и в этом случае список «' и т. д. выполнил бы эту работу, но OP сказал, что это не решение. – Flynn1179

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