Сортировка по атрибуту, полученному из справочного списка

Я пытаюсь отсортировать список категоризированных элементов xml, используя XSLT 2.0. Каждый элемент имеет уникальный идентификатор, а категоризация определяется в другом списке, содержащем эти и другие элементы. Вот пример начального XML-документа. Раздел, который я хочу отсортировать, - это / Atlas / VisitedCities. Его следует отсортировать по региону мира и дате посещения:

<?xml version="1.0" encoding="UTF-8"?>
<Atlas>
    <Cities>
        <City id="1" worldPart="Africa">
            <Name>Luxor</Name>
            <Founded>-3200</Founded>
            <Location>Egypt</Location>
        </City>
        <City id="2" worldPart="Africa">
            <Name>Tripoli</Name>
            <Founded>-700</Founded>
            <Location>Libya</Location>
        </City>
        <City id="3" worldPart="Americas">
            <Name>Cholula</Name>
            <Founded>-200</Founded>
            <Location>Mexico</Location>
        </City>
        <City id="4" worldPart="Americas">
            <Name>Flores</Name>
            <Founded>-1000</Founded>
            <Location>Guatemala</Location>
        </City>
        <City id="5" worldPart="Europe">
            <Name>Argos</Name>
            <Founded>-5000</Founded>
            <Location>Greece</Location>
        </City>
        <City id="6" worldPart="Europe">
            <Name>Athens</Name>
            <Founded>-4000</Founded>
            <Location>Greece</Location>
        </City>
    </Cities>
    <VisitedCities lastUpdate="2018-09-10">
        <VisitedCity cityID="6">
            <Date>1883-08-26</Date>
            <Visitor>Dora</Visitor>
        </VisitedCity>
        <VisitedCity cityID="3">
            <Date>1907-01-02</Date>
            <Visitor>Nemo</Visitor>
        </VisitedCity>
        <VisitedCity cityID="4">
            <Date>1940-02-08</Date>
            <Visitor>Jimenez</Visitor>
        </VisitedCity>
        <VisitedCity cityID="2">
            <Date>1886-06-10</Date>
            <Visitor>James T. Kirk</Visitor>
        </VisitedCity>
    </VisitedCities>
</Atlas>

Желаемый результат таков:

<?xml version="1.0" encoding="UTF-8"?>
<Atlas>
    <Cities>
        <City id="1" worldPart="Africa">
            <Name>Luxor</Name>
            <Founded>-3200</Founded>
            <Location>Egypt</Location>
        </City>
        <City id="2" worldPart="Africa">
            <Name>Tripoli</Name>
            <Founded>-700</Founded>
            <Location>Libya</Location>
        </City>
        <City id="3" worldPart="Americas">
            <Name>Cholula</Name>
            <Founded>-200</Founded>
            <Location>Mexico</Location>
        </City>
        <City id="4" worldPart="Americas">
            <Name>Flores</Name>
            <Founded>-1000</Founded>
            <Location>Guatemala</Location>
        </City>
        <City id="5" worldPart="Europe">
            <Name>Argos</Name>
            <Founded>-5000</Founded>
            <Location>Greece</Location>
        </City>
        <City id="6" worldPart="Europe">
            <Name>Athens</Name>
            <Founded>-4000</Founded>
            <Location>Greece</Location>
        </City>
    </Cities>
    <VisitedCities lastUpdate="2018-09-10">
        <VisitedCity cityID="2">
            <Date>1886-06-10</Date>
            <Visitor>James T. Kirk</Visitor>
        </VisitedCity>
        <VisitedCity cityID="6">
            <Date>1883-08-26</Date>
            <Visitor>Dora</Visitor>
        </VisitedCity>
        <VisitedCity cityID="3">
            <Date>1907-01-02</Date>
            <Visitor>Nemo</Visitor>
        </VisitedCity>
        <VisitedCity cityID="4">
            <Date>1940-02-08</Date>
            <Visitor>Jimenez</Visitor>
        </VisitedCity>
    </VisitedCities>
</Atlas>

Таблица стилей (XSLT 2.0), с которой я борюсь, выглядит так:

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

    <!-- Format output -->
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*" />

    <!-- Copy everything that does not match later templates. -->
    <xsl:template match="node()|@*" priority="-1">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:variable name="city.list" select="/Atlas/Cities"/>

    <xsl:variable name="sort-order" as="element()*">
        <wPart>Africa</wPart>
        <wPart>Europe</wPart>
        <wPart>Americas</wPart>
    </xsl:variable>

    <xsl:template match="/Atlas/VisitedCities">
        <xsl:variable name="city-list" select="."/>
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:for-each select="$sort-order">
                <xsl:variable name="this-wpart" select="./text()"/>
                <!-- How to select VisitedCity based on info in other list??? -->
                <xsl:apply-templates select="$city-list/VisitedCity[$city.list/City[@cityID=$city-list/VisitedCity/@cityID]/@worldPart=$this-wpart]">
                    <xsl:sort select="./Date"/>
                </xsl:apply-templates>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

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

Можете ли вы отредактировать свой вопрос, чтобы показать ожидаемый результат? Спасибо!

Tim C 10.09.2018 11:23

Вам нужно будет объяснить на простом английском языке и с образцом выходных данных, какую часть вы хотите отсортировать по каким критериям, ваш XSLT, который не работает (match="/VisitiedCities" даже не соответствует вашему образцу с корнем Atlas), не объясняет, что вы хотите добиться.

Martin Honnen 10.09.2018 11:24
0
2
48
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Может быть достаточно установить ключ для ссылки на элементы City с помощью атрибута id, чтобы затем в выражении xsl:sortselect ссылаться на атрибут worldPart. Кроме того, вы можете заменить for-each в вашем континентальном заказе на звонок index-of() с

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:key name="city-by-id" match="Cities/City" use="@id"/>

    <xsl:variable name="sort-order" as="element()*">
        <wPart>Africa</wPart>
        <wPart>Europe</wPart>
        <wPart>Americas</wPart>
    </xsl:variable>

  <xsl:template match="VisitedCities">
      <xsl:copy>
          <xsl:apply-templates select="VisitedCity">
              <xsl:sort select="index-of($sort-order, key('city-by-id', @cityID)/@worldPart)"/>
              <xsl:sort select="Date"/>
          </xsl:apply-templates>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/eiZQaFJ

Этот полный пример - XSLT 3, но чтобы использовать его с XSLT 2, вам нужно просто заменить там объявление xsl:mode своим шаблоном, который вы снабдили префиксом комментария <!-- Copy everything that does not match later templates. -->, то есть шаблоном преобразования идентичности.

Я не знал, что вы можете найти соответствие по «VisitedCities» или «Cities / City». Почему вам не нужен ведущий "/ Atlas /" или "//"?

Fredrik 10.09.2018 12:36

Шаблон соответствия соответствует определенному узлу, «если соответствующее выражение пути выберет узел» (или элемент в целом в XSLT 3), см. Разделы w3.org/TR/xslt-30/#rule-based-processing и w3.org/TR/xslt-30/#patterns. Хотя вы можете писать более конкретные шаблоны и для своего образца использовать абсолютные пути вниз от корня, например, match="/Atlas/Cities/City" или например match="/Atlas/VisitedCities", в шаблоне совпадения обычно бывает как можно короче и настолько конкретнее, насколько это необходимо.

Martin Honnen 10.09.2018 12:47

"VisitedCities" чем-то отличается от "// VisitedCities"? Мне кажется, что в обоих случаях пришлось бы обыскивать все дерево.

Fredrik 10.09.2018 12:49

В шаблоне Mach не имеет значения, пишете ли вы match="VisitedCities" или match="//VisitedCities", см. Спецификацию, в которой есть много примеров для начала, формальное определение, а также неформальный раздел, в котором, например, говорится: «Шаблон выбора, такой как book/chapter/section, можно исследовать справа. влево. Узел будет соответствовать этому шаблону, только если он является элементом section; и затем, только если его родительский элемент является chapter; и затем, только если родительский элемент этого chapter является book ».

Martin Honnen 10.09.2018 14:05

И «Когда в шаблоне используется оператор //, его все еще можно читать справа налево, но на этот раз тестируются предки узла, а не его родитель. Например, appendix//section соответствует каждому элементу section, имеющему элемент предка appendix».

Martin Honnen 10.09.2018 14:06

Другие вопросы по теме