Сортировка XSLT - как отсортировать дочерние узлы XML внутри родительского узла с атрибутом

То, что начиналось как простая вещь, оказалось довольно проблематичным для XSLT noob.

Пытаясь отсортировать дочерние узлы / нисходящие, но после добавления атрибута к их родительскому узлу я получаю сообщение об ошибке при отладке в VS2010:

"Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added."

Предположим, у меня есть этот простой XML:

<posts>
    <year value="2013">
        <post postid="10030" postmonth="1">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10040" postmonth="2">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10050" postmonth="3">
             <othernode></othernode>
             <othernode2></othernode2>
        </post>
    </year>
    <year value="2012">
        <post postid="10010" postmonth="1">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10015" postmonth="2">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10020" postmonth="3">
             <othernode></othernode>
             <othernode2></othernode2>
        </post>
    </year>
</posts>

Я передаю XPATH источнику xmldatasource для получения соответствующего <year> узел, например 2013. Тогда мне нужно отсортировать его дочерний <post> нисходящие узлы с использованием postid, поэтому для <year value=2013>, postid=10050 будет отображаться первым при визуализации.

Итак, чтобы быть ясным: меня интересует только сортировка внутри одного <year> узел.

До того, как я разделил узлы на отдельные узлы (т. Е. Xml был /posts/post), работал следующий XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
                <xsl:sort select="@postid" data-type="text" order="descending"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Теперь xmldatasource пуст при запуске из-за вышеуказанной ошибки. Если я перейду по возрастанию в порядке, тот же XML возвращается явно (без преобразования)

Вопрос: как обновить XSLT выше (или новый), чтобы он соответствовал атрибуту родительского узла (<year value="">)? В ходе исследования ответ сказал: "Мне нужно добавить создание атрибута перед созданием элемента". Это имеет смысл, так как при просмотре отладчика дочерние узлы формируются в порядке desc, но у тега year отсутствует свой атрибут. Но я не имею ни малейшего понятия о XSLT. Не могу видеть это слишком сложным, но просто не знаю языка.

Любая помощь, высоко ценится. Спасибо

2 ответа

Решение

Так вы говорите, что вы только передаете часть своего документа XML (один <year> узел) к процессору XSLT?

Вы должны использовать отдельный шаблон для года, так что это единственный шаблон, который использует сортировку. Как это следующее:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="year">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="post">
        <xsl:sort select="@postid" data-type="number" order="descending"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

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

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*">
        <xsl:apply-templates select="node()">
            <xsl:sort select="@postid" data-type="text" order="descending"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

Это преобразование:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="year">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates select="*">
      <xsl:sort select="@postid" data-type="number" order="descending"/>
    </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML:

<posts>
    <year value="2013">
        <post postid="10030" postmonth="1">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10040" postmonth="2">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10050" postmonth="3">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
    </year>
    <year value="2012">
        <post postid="10010" postmonth="1">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10015" postmonth="2">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
        <post postid="10020" postmonth="3">
            <othernode></othernode>
            <othernode2></othernode2>
        </post>
    </year>
</posts>

дает желаемый, правильный результат:

<posts>
   <year value="2013">
      <post postid="10050" postmonth="3">
         <othernode/>
         <othernode2/>
      </post>
      <post postid="10040" postmonth="2">
         <othernode/>
         <othernode2/>
      </post>
      <post postid="10030" postmonth="1">
         <othernode/>
         <othernode2/>
      </post>
   </year>
   <year value="2012">
      <post postid="10020" postmonth="3">
         <othernode/>
         <othernode2/>
      </post>
      <post postid="10015" postmonth="2">
         <othernode/>
         <othernode2/>
      </post>
      <post postid="10010" postmonth="1">
         <othernode/>
         <othernode2/>
      </post>
   </year>
</posts>
Другие вопросы по тегам