Fork me on GitHub

Tutorial 5

E05: Filtering a RSS feed

This example is to demonstrate how to modify a XML document. The Slashdot RSS feed is projected to this interface and the stories will be accessible via a subprojection. This time there is a setter for a collection of Stories which will replace the existing sequence of rss items. Instead of just setting an elements value like in the last example, we now change a sequence of elements. Notice that there are name spaces in the source XML document and that we work with them intuitively.

Projection API

public interface SlashdotRSSFeed {
 
    /**
     * We like to access each story as a java object, so we define a sub
     * projection here. Notice that this does not have to be an inner interface,
     * thats just to compact this tutorial.
     */
    interface Story {
        @XBRead("child::title")
        String getTitle();
 
        @XBRead("child::pubDate")
        String getDate();
 
        @XBRead("child::description")
        String getDescription();
    }
 
    /**
     * Our getter method with an XPath expression to select all RSS items. The
     * target type definition specifies this getter as returning a list of sub
     * projections.
     *
     * @return List of all stories
     */
    @XBRead("/rss/channel/item")
    List<Story> getAllItems();
 
    /**
     * Our setter uses exact the same XPath expression as the getter. Thus it
     * will replace the items returned by getAllItems(). Notice that it will
     * only replace XML elements named "item", because that is exactly what the
     * XPath is selecting. Other child elements of the channel element won't be
     * touched.
     *
     * Notice that we could define another setter which could project a story to
     * other elements than "item" in the document hierarchy.
     *
     * @param items
     */
    @XBWrite("/rss/channel/item")
    void setAllItems(Collection<Story> items);
     
    @XBRead("count(/rss/channel/item)")
    int getItemCount();
 
    /**
     * This is not part of this lesson about modifying documents. Just to
     * demonstrate the flexibility of projections. There is no need to strictly
     * keep the object oriented way to the stories. We just define a getter for
     * all creator elements of all stories without a sub projection.
     *
     * Notice the seamless use of the namespace. The projector will use the
     * namespaces declared in the document.
     *
     * @return A list of all creators in this feed.
     */
    @XBRead("//dc:creator")
    List<String> getCreators();
 
    /**
     * Another getter not part of this lesson. This time we let the projection
     * declaration do some filtering. Usually you would have to code this in
     * java.
     *
     * @return A list of open source stories.
     */
    @XBRead("/rss/channel/item[dc:subject=opensource]")
    List<Story> getOpenSourceStories();
}

Example Code

And here some unit tests to print out some data about our RSS feed.

~~
 This example is to demonstrate how to modify a XML document. The Slashdot RSS
 feed is projected to this interface and the stories will be accessible via a
 subprojection. This time there is a setter for a collection of Stories which
 will replace the existing sequence of rss items.
 Instead of just setting an elements value like in the last example, we now change a sequence of elements.
 Notice that there are name spaces in the source XML document and that we work with them intuitively.
public class TestFilterRSSFeed extends TutorialTestCase{
 
    private static SlashdotRSSFeed feed;
 
    @BeforeClass
    public static void readFeed() throws IOException {
       // XBProjector projector = new XBProjector();
       // feed = projector.io().fromURLAnnotation(SlashdotRSSFeed.class);
    }
 
    @Ignore
    public void printSomeStats() {
        Set<String> creators = new HashSet<String>(feed.getCreators());
        System.out.println("There are " + feed.getAllItems().size() + " stories by " + creators.size() + " different creators.");
    }
 
    /**
     * Remove all but the first three stories from a Slashdot RSS feed. Result
     * is formatted by standard Transformer capabilities.
     */
    @Ignore //Slashdot changed the rss format recently. TODO: Need to think about a different examle.
    public void filterSomeArticles() throws IOException {
        List<Story> filteredItems = new LinkedList<Story>();
        for (Story item : feed.getAllItems()) {
            filteredItems.add(item);
            if (filteredItems.size() == 3) {
                break;
            }
        }
 
        // This call removes all but the given items. Other child elements stay
        // untouched.
        feed.setAllItems(filteredItems);
 
        assertEquals(3,feed.getItemCount());
 
        // System.out.println(feed.toString());
    }
}