Делать разворот с помощью XSLT - PullRequest
5 голосов
/ 08 января 2009

У меня есть XML-файл, подобный этому:

<root>
    <item>
        <name>one</name>
        <status>good</status>
    </item>
    <item>
        <name>two</name>
        <status>good</status>
    </item>
    <item>
        <name>three</name>
        <status>bad</status>
    </item>
    <item>
        <name>four</name>
        <status>ugly</status>
    </item>
    <item>
        <name>five</name>
        <status>bad</status>
    </item>
</root>

Я хочу преобразовать это, используя XSLT, чтобы получить что-то вроде:

<root>
    <items><status>good</status>
        <name>one</name>
        <name>two</name>
    </items>
    <items><status>bad</status>
        <name>three</name>
        <name>five</name>
    </items>
    <items><status>ugly</status>
        <name>four</name>
    </items>
</root>

Другими словами, я получаю список предметов, каждый из которых имеет статус, и я хочу превратить его в список статусов, каждый со списком предметов.

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

Спасибо за любую помощь.

Ответы [ 4 ]

7 голосов
/ 08 января 2009

Мюнхен на помощь!

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>

    <xsl:key name="muench" match="/root/item/status" use="."/>

    <xsl:template match="/">
        <root>
        <xsl:for-each select="/root/item/status[generate-id() = generate-id(key('muench',.)[1])]">
            <xsl:call-template name="pivot">
                <xsl:with-param name="status" select="."/>
            </xsl:call-template>
        </xsl:for-each>
        </root>
    </xsl:template>

    <xsl:template name="pivot">
        <xsl:param name="status"/>
        <items>
            <status><xsl:value-of select="$status"/></status>
            <xsl:for-each select="/root/item[status=$status]">
                <name><xsl:value-of select="name"/></name>
            </xsl:for-each>
        </items>
    </xsl:template>

</xsl:stylesheet>
4 голосов
/ 08 января 2009

Да, это можно сделать в XSLT 1.0

<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="kStatByVal" 
         match="status" use="."/>
    <!--                                   -->
    <xsl:key name="kItemByStat" 
         match="item" use="status"/>
    <!--                                   -->
    <xsl:variable name="vDoc" select="/"/>

    <xsl:template match="/">
      <top>
        <xsl:for-each select=
        "/*/*/status[generate-id()
                    =
                     generate-id(key('kStatByVal',.)[1])
                    ]">
          <items>
            <status><xsl:value-of select="."/></status>
          <xsl:for-each select="key('kItemByStat', .)">
            <xsl:copy-of select="name"/>
          </xsl:for-each>
          </items>
        </xsl:for-each>
      </top>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к исходному документу XML :

<root>
    <item>
        <name>one</name>
        <status>good</status>
    </item>
    <item>
        <name>two</name>
        <status>good</status>
    </item>
    <item>
        <name>three</name>
        <status>bad</status>
    </item>
    <item>
        <name>four</name>
        <status>ugly</status>
    </item>
    <item>
        <name>five</name>
        <status>bad</status>
    </item>
</root>

Требуемый результат получен :

<top>
    <items>
        <status>good</status>
        <name>one</name>
        <name>two</name>
    </items>
    <items>
        <status>bad</status>
        <name>three</name>
        <name>five</name>
    </items>
    <items>
        <status>ugly</status>
        <name>four</name>
    </items>
</top>

Обратите внимание на использование :

  1. Метод Мюнхена для группировки

  2. Использование <xsl:key> и key() функция

0 голосов
/ 18 октября 2013

В XSLT 2.0 вы можете заменить группировку muenchian стандартным механизмом группировки. Применительно к данному ответу xslt будет выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>

    <xsl:key name="muench" match="/root/item/status" use="."/>

    <xsl:template match="/">
        <root>
        <xsl:for-each-group select="/root/item/status" group-by="key('muench', .)">
            <xsl:call-template name="pivot">
                <xsl:with-param name="status" select="."/>
            </xsl:call-template>
        </xsl:for-each-group>
        </root>
    </xsl:template>

    <xsl:template name="pivot">
        <xsl:param name="status"/>
        <items>
            <status><xsl:value-of select="$status"/></status>
            <xsl:for-each select="/root/item[status=$status]">
                <name><xsl:value-of select="name"/></name>
            </xsl:for-each>
        </items>
    </xsl:template>

</xsl:stylesheet>
0 голосов
/ 08 января 2009

Это зависит от вашего движка xslt. Если вы используете xslt 1.0 без каких-либо расширений, то ваш подход, безусловно, лучший.

С другой стороны, если вам разрешено использовать exslt (особенно расширение набора узлов) или xslt 2.0, то вы можете сделать это более общим способом:

  1. Соберите все доступные статусы
  2. Создать набор узлов из полученного результата
  3. Итерируя по этому набору узлов, создайте свою опорную точку, отфильтровывая базу статуса по текущему элементу в вашей итерации.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...