XSL, чтобы найти все узлы между узлами - PullRequest
3 голосов
/ 27 октября 2010

У меня есть большой плохо сформированный XML-файл, в котором информация, связанная с отдельной строкой, разбита на несколько строк информации, которые я пытаюсь сгруппировать с родительской позицией (ITEM_ID).Информация является последовательной, поэтому ключом является узел ITEM_ID, но я не могу создать надлежащий XSL, необходимый для группировки информации, связанной с элементом (ITEM_ID), учитывая следующий источник XML (обновлен, чтобы включать недавно обнаруженный элемент внука вИсточник XML):

<LINE_INFO>
    <ITEM_ID>some_part_num</ITEM_ID>
    <DESC>some_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
</LINE_INFO>
<LINE_INFO>
    <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>some_other_part_num</ITEM_ID>
    <DESC>some_other_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
</LINE_INFO>
<LINE_INFO>
    <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
</LINE_INFO>
<LINE_INFO>
    <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
</LINE_INFO>
<LINE_INFO>
    <ADDTL_NOTE_DETAIL>
        <NOTE>This is the grandchild note that sometimes appears in my data</NOTE>
    </ADDTL_NOTE_DETAIL>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>yet_another_part_num</ITEM_ID>
    <DESC>yet_another_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
</LINE_INFO>
  ...

Желаемый вывод:

<LINE_INFO>
    <ITEM_ID>some_part_num</ITEM_ID>
    <DESC>some_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
    <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>some_other_part_num</ITEM_ID>
    <DESC>some_other_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
    <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
    <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
    <NOTE>This is the grandchild note that sometimes appears in my data</NOTE>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>yet_another_part_num</ITEM_ID>
    <DESC>yet_another_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
</LINE_INFO>

Ответы [ 3 ]

2 голосов
/ 27 октября 2010

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kFollowing" match="LINE_INFO[not(ITEM_ID)]"
 use="generate-id(preceding-sibling::LINE_INFO[ITEM_ID][1])"/>

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

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

   <xsl:apply-templates select="key('kFollowing', generate-id())/node()"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="LINE_INFO[not(ITEM_ID)]"/>
</xsl:stylesheet>

при применении к предоставленному XML-документу (обернутый в один верхний элемент для правильной формы):

<t>
    <LINE_INFO>
        <ITEM_ID>some_part_num</ITEM_ID>
        <DESC>some_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
    <LINE_INFO>
        <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>some_other_part_num</ITEM_ID>
        <DESC>some_other_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
    <LINE_INFO>
        <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
    </LINE_INFO>
    <LINE_INFO>
        <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>yet_another_part_num</ITEM_ID>
        <DESC>yet_another_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
</t>

дает желаемый, правильный результат :

<t>
    <LINE_INFO>
        <ITEM_ID>some_part_num</ITEM_ID>
        <DESC>some_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
        <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>some_other_part_num</ITEM_ID>
        <DESC>some_other_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
        <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
        <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>yet_another_part_num</ITEM_ID>
        <DESC>yet_another_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
</t>

Обратите внимание : использование ключей для простой и эффективной идентификации всех LINE_INFO узлов, которые не имеют дочернего элемента ITEM_ID и сразу же следуют за узлом LINE_INFO с дочерним элементом ITEM_ID.

1 голос
/ 27 октября 2010

Эта таблица стилей XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="root">
        <xsl:for-each-group select="LINE_INFO"
                            group-starting-with="LINE_INFO[ITEM_ID]">
            <xsl:copy>
                <xsl:apply-templates select="current-group()/node()"/>
            </xsl:copy>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

С этим входом:

<root>
    <LINE_INFO>
        <ITEM_ID>some_part_num</ITEM_ID>
        <DESC>some_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
    <LINE_INFO>
        <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>some_other_part_num</ITEM_ID>
        <DESC>some_other_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
    <LINE_INFO>
        <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
    </LINE_INFO>
    <LINE_INFO>
        <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
    </LINE_INFO>
    <LINE_INFO>
        <ITEM_ID>yet_another_part_num</ITEM_ID>
        <DESC>yet_another_part_num_description</DESC>
        <QTY>nn</QTY>
        <UNIT>uom</UNIT>
    </LINE_INFO>
</root>

Вывод:

<LINE_INFO>
    <ITEM_ID>some_part_num</ITEM_ID>
    <DESC>some_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
    <EXT_DESC>more_description_for_some_part_num</EXT_DESC>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>some_other_part_num</ITEM_ID>
    <DESC>some_other_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
    <EXT_DESC>more_description_for_some_other_part_num</EXT_DESC>
    <LINE_NOTE>This is a note related to some_other_part_num</LINE_NOTE>
</LINE_INFO>
<LINE_INFO>
    <ITEM_ID>yet_another_part_num</ITEM_ID>
    <DESC>yet_another_part_num_description</DESC>
    <QTY>nn</QTY>
    <UNIT>uom</UNIT>
</LINE_INFO>
1 голос
/ 27 октября 2010

Это классическая проблема группировки.Лучший подход зависит от того, используете ли вы XSLT 2.0 или должны использовать 1.0.

Если 2.0, вы захотите использовать <xsl:for-each-group>:

<table>
   <xsl:for-each-group select="LINE_INFO" group-starting-with="LINE_INFO[ITEM_ID]">

Вышеуказанные выражения XPath для select и group-starting-with предполагают, что узел контекста является родителем элементов LINE_INFO.В качестве альтернативы вы можете поместить // в начале обоих выражений, рискуя снизить производительность.

Вывести строку для каждой группы, с данными, помещенными в ячейки таблицы в соответствии с вашим последним комментарием:

      <tr>
         <td><xsl:value-of select="current-group()/ITEM_ID" /></td>
         <td>
           <xsl:value-of "concat(current-group()/DESC, current-group()/EXT_DESC)"/>
           <br />
           <xsl:value-of "concat(current-group()/LINE_NOTE)" />
           <br />
           <xsl:value-of "concat(current-group()/NOTE)" />
         </td>
         <td><xsl:value-of select="current-group()/QTY" /></td>
         <td><xsl:value-of select="current-group()/ADDTL_NOTE_DETAIL/NOTE" /></td>
      </tr>
   </xsl:for-each-group>
</table>

(Остальная часть этого ответа несколько устарела, поскольку у ОП XSLT 2.0.)

Если 1,0, ваша лучшая ставка - мюнхенская группировка .Для шага идентификации групп (шаг 1) вы должны использовать ключ, такой как

<xsl:key name="LINE_INFO-by-section" match="LINE_INFO"
    use="generate-id((. | preceding-sibling::LINE_INFO)[ITEM_ID][last()])" />

Для перебора групп:

<xsl:for-each select="LINE_INFO[ITEM_ID]">
   <xsl:copy>

Для перебора членовгруппа:

      <xsl:variable name="section-starter-id" select="generate-id(.)" />
      <xsl:for-each select="key('LINE_INFO-by-section', $section-starter-id))">
         <xsl:copy-of select="node()|@*" />
      </xsl:for-each>
   </xsl:copy>
</xsl:for-each>

(не проверено)

...