Как использовать XSL для сортировки элементов по именам дочерних элементов

#xml #xslt

#xml #xslt

Вопрос:

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

Вот пример. Я упростил его по сравнению с моим фактическим требованием, так что, надеюсь, это не вызовет еще большей путаницы, которую оно решает. Реальное требование имеет ряд других элементов, как родных братьев и сестер, Animals с их собственными требованиями к сортировке, но этот фрагмент, в частности, доставляет мне огорчение.

Перед преобразованием

     <Animals>
      <Animal Value="20160.17">
        <Identifier id="1" />
        <Pig><!-- more elements inside here--></Pig>
      </Animal>
      <Animal Value="5000.40">
        <Identifier id="2" />
        <Cow><!-- more elements inside here--></Cow>
      </Animal>
      <Animal Value="63.44">
        <Identifier id="3" />
        <Pig><!-- more elements inside here--></Pig>
      </Animal>
      <Animal Value="4800.40">
        <Identifier id="4" />
        <Cow><!-- more elements inside here--></Cow>
      </Animal>
    </Animals>
 

Требуемое преобразование

Я бы хотел, чтобы каждый Animal элемент сортировался так, чтобы все Animal s с Cow дочерним элементом были первыми, а затем все Animal s с Pig дочерним элементом. В каждой из этих категорий они должны быть отсортированы численно по атрибуту Animal ‘s Value .

После преобразования

     <Animals>
      <Animal Value="4800.40">
        <Identifier id="4" />
        <Cow><!-- more elements inside here--></Cow>
      </Animal>
      <Animal Value="5000.40">
        <Identifier id="2" />
        <Cow><!-- more elements inside here--></Cow>
      </Animal>
      <Animal Value="63.44">
        <Identifier id="3" />
        <Pig><!-- more elements inside here--></Pig>
      </Animal>
      <Animal Value="20160.17">
        <Identifier id="1" />
        <Pig><!-- more elements inside here--></Pig>
      </Animal>
    </Animals>
 

Попытка
Я довольно новичок в XSL, и моя лучшая попытка до сих пор, похоже, не учитывает элементы Cow / Pig. Он сортирует только по Value атрибуту:

   <xsl:template match="Animals">
    <xsl:copy>
        <xsl:apply-templates select="Animal">
          <xsl:sort select="name(*) = 'Cow'" />
          <xsl:sort select="name(*) = 'Pig'" />
          <xsl:sort select="@Value" data-type="number" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

 

Ответ №1:

Ваша инструкция:

 <xsl:sort select="name(*) = 'Cow'" />
 

ничего не делает, потому что выражение:

 name(*)
 

возвращает имя первого дочернего элемента, которое является «Идентификатором» для всех Animal узлов.

Попробуйте, возможно:

 <xsl:sort select="name(*[2]) = 'Cow'" order="descending"/>
 

(Порядок должен быть descending таким, потому что в алфавитном порядке «true» идет после false».)

Кроме того, вы можете просто применять шаблоны отдельно для каждой категории:

 <xsl:template match="Animals">
    <xsl:copy>
        <xsl:apply-templates select="Animal[Cow]">
            <xsl:sort select="@Value" data-type="number" />
        </xsl:apply-templates>
        <xsl:apply-templates select="Animal[Pig]">
            <xsl:sort select="@Value" data-type="number" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>
 

Комментарии:

1. спасибо, я выбрал второй вариант separate apply-templates , поскольку порядок дочерних элементов не гарантируется. я действительно пробовал что-то подобное ранее, но неправильно определил значение select атрибута. похоже, мне нужно поработать над своими навыками xpath, а не над своими навыками xslt…