Перегруппировать XML-братьев и сестер с помощью XSLT - PullRequest
0 голосов
/ 30 октября 2018

Я использую XSLT для преобразования XML-документа в новый XML-документ с большим значением. Исходный XML:

<root>
  <A>
    <country>Italy</country>
    <city>Rome</city>
    <score>13</score>
  </A>
  <A>
    <country>Italy</country>
    <city>Florence</city>
    <score>14</score>
  </A>
  <A>
    <country>France</country>
    <city>Paris</city>
    <score>20</score>
  </A>
</root>

Узлы <country>, <city> и <score> - все братья и сестры. Мой вопрос: как я могу переставить братьев и сестер, как это в XSLT?

<country>
  <city>
    <score>
    </score>
  </city>
</country>

Мой ожидаемый XML:

<root>
  <Italy>
    <Rome>
      <score>13</score>
    </Rome>
    <Florence>
      <score>14</score>
    </Florence>
  </Italy>
  <France>
    <Paris>
      <score>20</score>
    </Paris>
  </France>
</root>

1 Ответ

0 голосов
/ 30 октября 2018

Решение XSLT-1.0 заключается в следующем. Он использует Muenchian Grouping в качестве метода для получения уникальных значений страны.

EDIT:
Чтобы убедиться, что имена элементов являются действительными QNames, я добавил выражение translate(...), которое преобразует все пробелы в названии соответствующего города или страны в подчеркивание.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="countries" match="A" use="country" />

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

    <xsl:template match="text()" />    

    <xsl:template match="A[generate-id(.) = generate-id(key('countries',country)[1])]">
        <xsl:element name="{translate(country,' ','_')}">
            <xsl:for-each select="key('countries',country)">
                <xsl:element name="{translate(city,' ','_')}">
                    <xsl:copy-of select="score" />
                </xsl:element>           
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Решение XSLT-2.0 проще, поскольку оно может использовать xsl:for-each-group:

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

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each-group select="A" group-by="country">
                <xsl:element name="{translate(current-grouping-key(),' ','_')}">
                    <xsl:for-each select="current-group()">
                        <xsl:element name="{translate(city,' ','_')}">
                            <xsl:copy-of select="score" />
                        </xsl:element>           
                    </xsl:for-each>
                </xsl:element>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Вывод обоих подходов одинаков:

<?xml version="1.0"?>
<root>
    <Italy>
        <Rome>
            <score>13</score>
        </Rome>
        <Florence>
            <score>14</score>
        </Florence>
    </Italy>
    <France>
        <Paris>
            <score>20</score>
        </Paris>
    </France>
</root>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...