2

I'm fairly new to xsl and am trying to find a way to hide, or mute, all but two nodes in an wordpress rss feed's xml, structured like so:

?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
    <title>Title</title>
    <atom:link href="http://www.alink.com" rel="self" type="application/rss+xml" />
    <link>http://www.alink.com</link>
    <description>Just another WordPress site</description>
    <lastBuildDate>Sun, 21 Apr 2013 22:13:55 +0000</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <generator>http://wordpress.org/?v=3.5.1</generator>

    <item>
        <title>A Title</title>
        <link>http://www.alink.com</link>
        <comments>comments</comments>
        <pubDate>Sun, 21 Apr 2013 22:13:55 +0000</pubDate>
        <dc:creator>aUser</dc:creator>
        <category><![CDATA[Uncategorized]]></category>
        <guid isPermaLink="false">http://www5.uifoundation.org/news/?p=112</guid>
        <description><![CDATA[Post[...]]]></description>
        <content:encoded> Posted October 10, 2013 </content:encoded>
        <wfw:commentRss>http://www.alink.com</wfw:commentRss>
        <slash:comments>0</slash:comments>
    </item>

But I want to display only the channel/title and channel/link in my transformation. I was thinking something along the lines of using an empty template on all the nodes, like this

<xsl:template match="channel/* EXCEPTION channel/item/title | channel/item/link" />

I'm unsure of how to declare the exception though. If there's a better way of doing this, I'd be open to that too

I really just want the output to be an unordered list of item/title, with the value of item/link, with everything else hidden. An example of the output would be:

<ul>
  <li>
    <a href= "www.aLink.com">A Title</a>
  </li>
</ul> 
Borodin
  • 126,100
  • 9
  • 70
  • 144
HawkEyeNick
  • 23
  • 1
  • 4
  • 1
    Do you want `rss` and `channel` also included in the output? An example of the exact output wanted would be helpful. – Daniel Haley Apr 22 '13 at 14:43
  • I really just want the output to be an unordered list of item/title, with the value of item/link, everything else hidden, so an example of the output would be: – HawkEyeNick Apr 22 '13 at 15:19
  • It would be good to edit your question to show the desired output. I think now that I understand what output you want, the answer will be completely different. – LarsH Apr 22 '13 at 15:23

1 Answers1

5

You can do it using separate templates, some of which are empty, and with different priorities:

<xsl:template match="channel/*" priority="0" /> <!-- swallow these, no output -->

<xsl:template match="channel/item | channel/link" priority="1">
   <xsl:copy-of select="." /> <!-- copy to output -->
</xsl:template>

In the case of channel/item and channel/link, the second template will override the first because of its higher priority. In the case of other children of channel, the first template will fire.

(Note that templates have a default priority based on the type of match pattern; and there are also rules for what to do if two templates with equal priority match the same node. However if you make priority explicit wherever there is potential for two templates to match the same node, you won't have to fiddle with those unseen rules.)

In the absence of other templates specifying what to do with <rss> and <channel>, the default template will apply, and they will be processed but not copied to the output, except for text nodes. That would be a bit of a mess. If you want to copy those two elements (but not all their descendants) to the output, try this template:

<xsl:template match="rss | channel">
  <xsl:copy>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:template>

EDIT

Now that the desired output has been specified, I would say the approach should be completely different. Your first template can be:

<xsl:template match="/">
  <ul>
    <xsl:apply-templates select="rss/channel/item" />
  </ul>
</xsl:template>

and your second template transforms each item into an <li>:

<xsl:template match="item">
  <li>
    <a href="{link}"><xsl:value-of select="title" /></a> 
  </li>
</xsl:template>

This is more of a "pull" rather than a "push" approach. You want specific nodes from the input, and the input's structure is highly predictable. By using specific apply-templates select expressions, instead of the generic <xsl:apply-templates select="*" />, you avoid the need for exception templates, because only the nodes you select will be processed.

LarsH
  • 27,481
  • 8
  • 94
  • 152
  • Thank you, this was very helpful. I was wondering though if there is a limit to how deep of a node I can enter in the template statement, for example I realized after using your reply that I'm really trying to select the channel/item/title and display it with item/link's value. However, the second slash seems to cause my transformation problems, is that legal syntax? – HawkEyeNick Apr 22 '13 at 15:11
  • There is no hard limit on the number of locations steps (`/`)in a match pattern. The syntax is legal. You may want to specify what problems your transformation is having. – LarsH Apr 22 '13 at 15:23
  • One more question if you don't mind me bugging you, is there a smooth way to then limit the number of items shown, say only the first 3? Again, thanks for your help. All worked liked a charm – HawkEyeNick Apr 22 '13 at 16:54
  • 2
    @HawkEyeNick Please remember to mark Lars' answer as accepted by clicking the checkmark next to it. – JLRishe Apr 22 '13 at 16:58
  • @HawkEyeNick: for that, change the apply-templates to: ``. That will give you the first three ``s of each ``. – LarsH Apr 22 '13 at 18:18