Сортировка 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>