Основным вкладом этого ответа является решение (в конце), которое можно использовать с бесконечным числом форматов , просто указав все «входные» альтернативные имена во внешнем (глобальном) параметре $postElements
и все альтернативные имена «дата публикации» во внешнем (глобальном) параметре $pub-dateElements
.
Кроме этого , здесь описано, как указать выражение XPath, которое выбирает все /rss//item
и все /feed//entry
элементов.
В простом случае только двух возможных форматов документов это (как предложено @Josh Davis) выражение Xpath работает правильно:
/rss//item | /feed//entry
Более общее выражение XPath позволяет выбирать нужные элементы из набора неограниченного числа форматов документов :
/*[contains($topElements, concat('|',name(),'|'))]
//*[contains($postElements, concat('|',name(),'|'))]
, где переменная $topElements
должна быть заменена настрока с разделителями в виде трубы всех возможных имен для верхнего элемента, и $postElements
должна быть заменена на строку с разделителями в виде трубы всех возможных имен для элемента "entry".Мы также допускаем, чтобы элементы "entry" находились на разной глубине в разных форматах документа.
В частности, для этого конкретного случая выражение XPath будет:
/*[contains('|feed|rss|', concat('|',name(),'|'))]
//*[contains('|item|entry|', concat('|',name(),'|'))]
В оставшейся части этого поста показано, как полностью и полностью выполнить требуемую обработку можно в XSLT - легко и элегантно.
I.A осторожный введение
Такая обработка легко и просто с XSLT :
<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="/">
<myFeed>
<xsl:apply-templates/>
</myFeed>
</xsl:template>
<xsl:template match="channel|feed">
<xsl:apply-templates select="*">
<xsl:sort select="pubDate|published" order="descending"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="item|entry">
<post>
<xsl:apply-templates mode="identity"/>
</post>
</xsl:template>
<xsl:template match="pubDate|published" mode="identity">
<publicationDate>
<xsl:apply-templates/>
</publicationDate>
</xsl:template>
<xsl:template match="node()|@*" mode="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="identity"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
, когда применяется это преобразованиек этому XML-документу (в формате 1):
<rss>
<channel>
<item>
<pubDate>2011-06-05</pubDate>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</item>
<item>
<pubDate>2011-06-06</pubDate>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</item>
<item>
<pubDate>2011-06-07</pubDate>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</item>
</channel>
</rss>
и когда он применяется к этому эквивалентному документу (в формате 2):
<feed>
<entry>
<published>2011-06-05</published>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</entry>
<entry>
<published>2011-06-06</published>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</entry>
<entry>
<published>2011-06-07</published>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</entry>
</feed>
в обоих случаях требуется один и тот же требуемый, правильный результат :
<myFeed>
<post>
<publicationDate>2011-06-07</publicationDate>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</post>
<post>
<publicationDate>2011-06-06</publicationDate>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</post>
<post>
<publicationDate>2011-06-05</publicationDate>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</post>
</myFeed>
II.Полное решение
Это может быть обобщено до параметризованного решения :
<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:param name="postElements" select=
"'|entry|item|'"/>
<xsl:param name="pub-dateElements" select=
"'|published|pubDate|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="identity"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<myFeed>
<xsl:apply-templates select=
"//*[contains($postElements, concat('|',name(),'|'))]">
<xsl:sort order="descending" select=
"*[contains($pub-dateElements, concat('|',name(),'|'))]"/>
</xsl:apply-templates>
</myFeed>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test=
"contains($postElements, concat('|',name(),'|'))">
<post>
<xsl:apply-templates/>
</post>
</xsl:when>
<xsl:when test=
"contains($pub-dateElements, concat('|',name(),'|'))">
<publicationDate>
<xsl:apply-templates/>
</publicationDate>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="identity"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Это преобразование может использоваться с бесконечным числом форматов , просто указав все «входные» альтернативные имена во внешнем (глобальном) параметре $postElements
и все «опубликованные» альтернативные имена во внешнем (глобальном) параметре $pub-dateElements
.
Каждый может попробоватьэто преобразование для проверки того, что при применении к двум вышеуказанным XML-документам оно снова дает тот же, требуемый и правильный результат.