XSLT 1.0 — Группировка XML-элементов

#xml #xslt #xpath

#xml #xslt #xpath

Вопрос:

Необходимо преобразовать запрос из системы A в fint в запрос системы B.

Предположим, у меня есть XML-документ, который выглядит следующим образом из системы A:

 <root>
<Bundle>
    <authors>
        <author>
            <authorID>100</authorID>
            <authorName>Kathisiera</authorName>
        </author>
        <author>
            <authorID>200</authorID>
            <authorName>Bates</authorName>
        </author>
        <author>
            <authorID>300</authorID>
            <authorName>Gavin King</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Head First Java</bookName>
            <bookRefID>100</bookRefID>
        </book>
        <book>
            <bookOrderID>5555</bookOrderID>
            <bookName>Head First Servlets</bookName>
            <bookRefID>200</bookRefID>
        </book>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Hibernate In Action</bookName>
            <bookRefID>300</bookRefID>
        </book>
    </books>
</Bundle>
 

Я должен вписать этот запрос в структуру запросов system B:

 <root>
<Bundle>
    <authors>
        <author>
            <authorID>100</authorID>
            <authorName>Kathisiera</authorName>
        </author>
        <author>
            <authorID>300</authorID>
            <authorName>Gavin King</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Head First Java</bookName>
            <bookRefID>100</bookRefID>
        </book>
        <book>
            <bookOrderID>1111</bookOrderID>
            <bookName>Hibernate In Action</bookName>
            <bookRefID>300</bookRefID>
        </book>
    </books>
</Bundle>
<Bundle>
    <authors>
        <author>
            <authorID>200</authorID>
            <authorName>Bates</authorName>
        </author>
    </authors>
    <books>
        <book>
            <bookOrderID>5555</bookOrderID>
            <bookName>Head First Servlets</bookName>
            <bookRefID>200</bookRefID>
        </book>
    </books>
</Bundle>
 

Сначала я должен сгруппировать book внутри Bundle на основе bookOrderID . Затем группируйте author внутри Bundle , сравнивая bookRefID с authorID .

Я попытался использовать key() generate-id() функцию xslt. Но не удалось получить ожидаемый результат.

Пожалуйста, помогите мне найти решение.

Ответ №1:

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

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

    <xsl:key name="k" match="book" use="bookOrderID"/>
    <xsl:key name="a" match="author" use="authorID"/>

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:apply-templates select="//books"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="books">

        <xsl:apply-templates select="book[generate-id(.) = generate-id(key('k', bookOrderID))]"/>

    </xsl:template>

    <xsl:template match="book">
        <Bundle>
            <authors>
                <xsl:apply-templates select="key('a', key('k', bookOrderID)/bookRefID)"/>
            </authors>

            <books>
                <xsl:copy-of select="key('k', bookOrderID)"/>
            </books>
        </Bundle>
    </xsl:template>

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

</xsl:stylesheet>
 

Ввод:

 <root>
    <Bundle>
        <authors>
            <author>
                <authorID>100</authorID>
                <authorName>Kathisiera</authorName>
            </author>
            <author>
                <authorID>200</authorID>
                <authorName>Bates</authorName>
            </author>
            <author>
                <authorID>300</authorID>
                <authorName>Gavin King</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Head First Java</bookName>
                <bookRefID>100</bookRefID>
            </book>
            <book>
                <bookOrderID>5555</bookOrderID>
                <bookName>Head First Servlets</bookName>
                <bookRefID>200</bookRefID>
            </book>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Hibernate In Action</bookName>
                <bookRefID>300</bookRefID>
            </book>
        </books>
    </Bundle>
</root>
 

Вывод:

 <root>
    <Bundle>
        <authors>
            <author>
                <authorID>100</authorID>
                <authorName>Kathisiera</authorName>
            </author>
            <author>
                <authorID>300</authorID>
                <authorName>Gavin King</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Head First Java</bookName>
                <bookRefID>100</bookRefID>
            </book>
            <book>
                <bookOrderID>1111</bookOrderID>
                <bookName>Hibernate In Action</bookName>
                <bookRefID>300</bookRefID>
            </book>
        </books>
    </Bundle>
    <Bundle>
        <authors>
            <author>
                <authorID>200</authorID>
                <authorName>Bates</authorName>
            </author>
        </authors>
        <books>
            <book>
                <bookOrderID>5555</bookOrderID>
                <bookName>Head First Servlets</bookName>
                <bookRefID>200</bookRefID>
            </book>
        </books>
    </Bundle>
</root>
 

Ответ №2:

Вот один из способов сделать это:

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

  <xsl:key name="kBook" match="book" use="bookOrderID" />

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

  <xsl:template match="root">
    <xsl:apply-templates mode="bundle" select="Bundle/books/book[
      generate-id()
      =
      generate-id(key('kBook', bookOrderID)[1])
    ]" />
  </xsl:template>

  <xsl:template match="book" mode="bundle">
    <xsl:variable name="bookGroup" select="key('kBook', bookOrderID)" />
    <Bundle>
      <authors>
        <xsl:copy-of select="//author[authorID = $bookGroup/bookRefID]" />
      </authors>
      <books>
        <xsl:copy-of select="$bookGroup" />
      </books>
    </Bundle>
  </xsl:template>
</xsl:stylesheet>
 

Это группирует книги по их bookOrderID использованию <xsl:key> .

После этого он использует свойство = оператора для поиска всех связанных авторов: = оператор сравнивает все узлы с обеих сторон друг с другом. Думайте об этом как о своего рода «внутреннем соединении» для наборов узлов. Таким образом, вы можете скопировать нужные узлы с помощью простого <xsl:copy-of> .