Skip to main content

Voice enabling XML, Part 1: Develop a voice-enabled RSS reader

Convert RSS feeds to voice and topic requests to voice instructions with VoiceXML

Martin Brown, Developer and writer, Freelance
Photo of Martin Brown
Martin Brown has been a professional writer for over eight years. He is the author of numerous books and articles across a range of topics. His expertise spans myriad development languages and platforms -- Perl, Python, Java, JavaScript, Basic, Pascal, Modula-2, C, C++, Rebol, Gawk, Shellscript, Windows, Solaris, Linux, BeOS, Mac OS/X and more -- as well as Web programming, systems management and integration. Martin is a regular contributor to ServerWatch.com, LinuxToday.com and IBM developerWorks, and a regular blogger at Computerworld, The Apple Blog and other sites, as well as a Subject Matter Expert (SME) for Microsoft. He can be contacted through his Web site at http://www.mcslp.com.

Summary:  RSS is a hot topic these days, as it provides an easy way to stream data online. This article, the first of a four-part series on developing VoiceXML applications, shows you how to develop a voice-enabled RSS reader. The input to the application is RSS data, and the output is VoiceXML that can be read and spoken by your favorite compatible voice application.

View more content in this series

Date:  21 Aug 2007
Level:  Intermediate
Activity:  1167 views
Comments:  

Introduction

Anyone interested in taking advantage of the many uses of a voice-enabled RSS reader will benefit from reading this article. You will also learn about VoiceXML basics and the RSS XML format, in addition to:

  • Translating RSS to VXML using XSLT
  • Writing a Perl script to generate the VXML
  • Adding interactivity to your VXML file
  • Generating VXML using Java™ servlets

About this series

Voice, and audio in general, is more and more popular on the Web. Examples include the plethora of music and webcasts currently available online. This series shows developers several ways to combine voice and XML to develop useful applications:

  • Part 1—a voice-enabled RSS reader
  • Part 2 —a voice-enabled calendar
  • Part 3 —a voice-enabled blogging and Twitter application
  • Part 4 —an voice-enabled Yahoo search application

VoiceXML basics

VoiceXML is the name given to the standard for voice-based XML output, although the file format itself is called VXML.

VXML itself is incredibly powerful when married with a suitable VoiceXML browser that will convert the VoiceXML content into text [that is, Text-To-Speech (TTS)] and is also capable of recognizing voice commands (voice recognition).

Listing 1 show the basic format for a VXML file.


Listing 1. Basic format for a VXML file
                
<?xml version="1.0"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.0">
...
</vxml>

You then embed form elements to introduce a selection of information and separate this into blocks. TTS is embedded within a <prompt> tag. Hence you can produce a simple spoken sentence using the VXML in Listing 2.


Listing 2. Sample VXML file
                
<?xml version="1.0" encoding="UTF-8"?>

<vxml version="2.1">
  <form>
    <block>
      <prompt>
        I could ask you anything!
      </prompt>
    </block>
  </form>
</vxml>

Prompts that expect a response can either provide a list of valid words that are accepted and recognized by the voice recognition system, or you can ask for input through the telephone keypad. The actual information is then stored in variables and you can use a simple if/else set of conditional statements to output different responses.

You'll use all of these different methods throughout the rest of this article and throughout the rest of this series as well.

To use the examples in this article you need to have access to a Web hosting service where you can provide the VXML files, and a VoiceXML browser that provides the TTS and VR components, usually over some form of phone line. Voxeo can provide the voice browser services you need and enables you to dial into your applications over land lines (using a special pin code), with a VoIP service that is based on the Session Initiation Protocol (SIP) and even using Skype. See Resources for more information.


Understanding the RSS source format

Really Simple Syndication (RSS) is an XML-based solution for publishing information that is often used with blogs and other sites. The RSS format enables you to generate an easy list of articles or information, and you can combine the sources of many RSS feeds (aggregation). The result is an easily formatted list of articles or stories, with an URL (for the full article), summary information, and classification information for each item. The RSS feed as a whole also includes classification and further detail and classification data.

RSS is just one of a number of different syndication formats in XML. Within an RSS file the basic structure is quite straightforward. At first viewing that can be difficult to follow, so a simplified structure looks something like that shown in Listing 3.


Listing 3. Sample RSS structure
                
<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.0.4" -->
<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/"
  >

<channel>
  <title>MCslp</title>
  <link>http://mcslp.com</link>
  <description>News from the desk of Martin MC Brown</description>
  <pubDate>Thu, 19 Apr 2007 08:14:30 +0000</pubDate>
  <generator>http://wordpress.org/?v=2.0.4</generator>
  <language>en</language>
    <item>
      <title>IBM developerWorks Podcast Interview</title>
      ...
    </item>
    <item>
      <title>...</title>
      ...
    </item>
</channel>
</rss>

The header content describes the information within the feed overall—in this case, you can see that I've given my blog a title of MCslp and a description of "News from the desk of Martin MC Brown".

The remainder of Listing 3 provides additional information for the feed, such as the publication date (which for a dynamic feed such as one from a blog is when the RSS XML was generated) and then a block for each individual item.

Each item block has a simple title, a summary set of information and, if the RSS feed supports it, a more detailed (and sometimes complete) version of the story. Here in Listing 4, you see the format for each item.


Listing 4. An RSS item in XML
                
<item>
  <title>IBM developerWorks Podcast Interview</title>
  <link>http://mcslp.com/?p=250</link>
  <comments>http://mcslp.com/?p=250#comments</comments>
  <pubDate>Thu, 19 Apr 2007 08:14:28 +0000</pubDate>
  <dc:creator>Martin MC Brown</dc:creator>

  <category>Articles</category>
  <category>Interviews</category>
  <category>IBM developerWorks</category>
  <category>Grids</category>
  <guid isPermaLink="false">http://mcslp.com/?p=250</guid>
  <description>
    <![CDATA[
      Summary
    ]]>
  </description>
  <content:encoded>
    <![CDATA[
      Full information
    ]]>
  </content:encoded>
</item>

For a VoiceXML version of the information, you can safely ignore some of these elements. There are limitations to what you can sensibly do when reading something out as a vocal version of the information—the categories for example are probably meaningless, and the guid and other elements are really for internal use only.

The really useful parts of the information are the title for the entire feed, the title for each item and the description or content for each item.

Let's look at a very simple method for translating an RSS file into VoiceXML.


Using a simple XSL transform

One of the quickest and simplest ways to convert an existing RSS XML file into VXML is to use an XSL template to convert the RSS XML into the necessary output. Some limitations with this process make certain operations very difficult, mostly because of the limited and sometimes convoluted methods available for selecting elements and making decisions within XSL, but it can be a good quick solution.

Listing 5 shows a simple XSL stylesheet that will convert the title and description elements of the RSS source file format into VXML to read out the headlines and content of each item within the RSS feed.


Listing 5. An XSL stylesheet for translating RSS to VXML
                
<?xml version="1.0" encoding ="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="no"/>
    <xsl:template match="/">

        <vxml version="2.0"
            xmlns="http://www.w3.org/2001/vxml">
            <form id="news">
                <xsl:for-each select="rss/channel/item">

                    <block>
                        <prompt>
                            <xsl:value-of select="title" />
                            <break size="small" />
                            <xsl:value-of select="description" />

                            <break size="medium" />
                        </prompt>
                    </block>

                </xsl:for-each>
            </form>
        </vxml>
    </xsl:template>
</xsl:stylesheet>

The XSL stylesheet processes all of the items within the RSS using an XPath definition to pick out the items. You then insert the value of the title and description into a prompt block, separating the two using a break block. Within VoiceXML, the break tag introduces a pause into the TTS output.

You can run the stylesheet past an RSS XML (in the example below, from the BBC news Web site) document using xsltproc, as shown in Listing 6.


Listing 6. Generating VXML with your XSL stylesheet
                
$ xsltproc rsstovxml.xsl rss.xml
<?xml version="1.0"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.0">
<form id="news">
  <block>
    <prompt>Prince Harry not to serve in Iraq
      <break size="small"/>
        Prince Harry will not be deployed in Iraq because of 
        the security threat, the head of the Army says.
      <break size="medium"/>
    </prompt>
  </block>
...
</form>
</vxml>

As you can see, for each item you output a block, the title, a pause, and the summary content for each item in the feed. You can upload the generating VXML to your browser and have a listen to the output. The result is not very practical, as you would need to add a download solution and automatically generate the file periodically to make it useful. So you need to think about how to produce the necessary information more dynamically.


The RSS to VoiceXML workflow

One issue with the XSL stylesheet process is that it is very simple but also quite limited, both in your options for generating the VXML file, and, ultimately, in the VXML generated. To enhance that, you can use the Perl or Java programming languages to do the conversion, which allows a little more complexity in the generation process.

Before you do that, let's imagine a simple structure for how an RSS browser might work. You can see an example of the process in Figure 1.


Figure 1. Workflow for an RSS to VXML application
Workflow for an RSS to VXML application

Basically, when the number is called, you provide the caller with a list of the available RSS feeds. When the user selects a feed, you then process the feed contents and list the headlines for each item in the feed. The caller can then select and listen to an individual news story, for the more detailed summary.

To start, you process a feed and output the VXML to read out the stories to generate the main list of information.


Generating VoiceXML from RSS with Perl

Perl has a number of different modules available to process syndication feeds, but by far the most practical is the XML::FeedPP module. This will process any syndication feed, including the main RSS, RDF and Atom formats, and provides the content through a simplified interface.

To parse a feed, supply the feed URL when you create a new XML::FeedPP object: my $feed = XML::FeedPP->new($feedurl);.

The title and other information about the feed can be accessed from this top-level object. For example, to get the feed title: $feed->title();.

To get the individual items within the feed, you use the get_item() method to get a list of item objects, and then use further methods (such as title() or description()) to get the actual content of each item.

You can therefore produce a list of news items from a given feed using the code shown in Listing 7.


Listing 7. Getting a VXML file from an RSS feed with Perl
                
use XML::FeedPP;

my $feedurl 
   = 'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml';

my $feed = XML::FeedPP->new($feedurl);

my ($selection) = ('');

$selection = '<form><block>';
$selection .= '<prompt>' . $feed->title() . 
              '<break size="small"/></prompt>';

foreach my $i ( $feed->get_item() )
{
    next unless defined($i);
    next unless ($i->link() =~ m/http/);

    $selection 
       .= sprintf('<prompt>%s.<break size="small"/></prompt>',
                  $i->title());
}

$selection .= '</block></form>';

print <<EOF;
<?xml version="1.0" encoding ="UTF-8"?>
<!DOCTYPE vxml PUBLIC "-//W3C//DTD VOICEXML 2.1//EN" 
              "http://www.w3.org/TR/voicexml21/vxml.dtd">
<vxml version="2.1" xmlns="http://www/w3/org/2001/vxml"
    xml:lang="en-US">
$selection
</vxml>
EOF

This script in Listing 7 has the two main parts. The first gets the feed and outputs the prompt blocks for each news item title; this is sequentially appended to a string. The second part then embeds the entire string into the core of a VXML file.

For each item, you iterate over the list of items, and output the title, followed by a small audio pause. You might use a full-blown XML generator to output this information, but while the generation of the XML is relatively simple, you can just use standard print statements to generate the information.

Note as well that the RSS feed is hardcoded and that the output is generated to standard out, so if you want it in a file you'll need to redirect the output to a file. The resulting VXML—trimmed here for space reasons—is shown in Listing 8.


Listing 8. Automatically generated VXML from an RSS feed
                
<vxml version="2.1" xmlns="http://www/w3/org/2001/vxml"
  xml:lang="en-US">

  <form>
    <block>
      <prompt>
        BBC News | UK | UK Edition
        <break size="small" />
      </prompt>
      <prompt>
        Prince Harry not to serve in Iraq.
        <break size="small" />
      </prompt>
      <prompt>
        Madeleine fighting fund launched.
        <break size="small" />
      </prompt>
...
    </block>
  </form>

</vxml>

If you upload this to your VoiceXML compatible service and then dial-in, you should be able to hear a list of the news stories directly from the feed.

With this example, you're still only getting the headlines. What would be nice is to hear the headlines and then have the option to the hear the summary of the news item.


Adding story selection and more detail

To add options and the summary feature, you need to do a few things when you generate the VXML:

  1. You need to create a list of options (with a number before or after them), followed by the functionality to accept a number for listening.
  2. You need to be able to parse an incoming number. You could do this by voice alone, but for this example you'll accept DTMF input.
  3. You need to provide the full story output.

The first item in that list is straightforward—you merely need to capture a unique number and output the number along with the story header. To ensure that the story header (and its number) matches the full summary when a number is selected, you'll use two temporary strings. One will contain the possible selections, and the other will contain the detailed output.

To capture a number from the keypad, you use a field specification to capture the input within the VXML: <field name="select_num" type="digits">.

Now the system will wait until the user enters some digits. When the variable is filled, the filled block in the VXML is parsed. This will be used to select the news summary information to output, and then output the appropriate block of text to be spoken.

To parse the value, you capture the inputted digits from the Dual Tone Multi-Frequency (DTMF) tones and then place that into a variable: <assign name="selection" expr="select_num"/>.

You do it this way so that you can accept multiple digits, although for the purposes of the demonstration you'll only output the top six stories.

Once you have the supplied digits, you can use an if block to select the right story to be played.

You can see the full script, again here in Perl, in Listing 9.


Listing 9. The full Perl script
                
use XML::FeedPP;

my $feedurl 
   = 'http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml';

my $feed = XML::FeedPP->new($feedurl);

my ($selection,$detail,$counter) = ('','',0);

$selection = '<form id="MainMenu"><field name="select_num" type="digits">';
$selection .= '<prompt>' . $feed->title() . 
                          '<break size="small"/></prompt>';
$selection .= '<prompt>Please select a story from the 
                           following list.</prompt>';

foreach my $i ( $feed->get_item() )
{
    next unless defined($i);
    next unless ($i->link() =~ m/http/);

# We only list the top six stories

    last if ($counter++ >= 6);

    if ($counter == 1)
    {
        $detail .= '<filled><assign name="selection" expr="select_num"/>';
        $detail .= "<if cond=\"selection =='$counter'\">";
    }
    else
    {
        $detail .= "<elseif cond=\"selection =='$counter'\"/>";
    }
    $detail .= sprintf('<prompt>%s. %s<break size="small"/>
<reprompt/></prompt>',$i->title(),$i->description());

    $selection .= sprintf('<prompt>%d: %s<break 
size="small"/></prompt>',$counter,$i->title());
}

$selection .= '<noinput>Please select a number. 
                              <reprompt/></noinput>';
$selection .= '<nomatch>Please select a valid number. 
                              <reprompt/></nomatch>';
$selection .= '</field>';

$detail .= '</if><clear 
namelist="select_num"/><reprompt/></filled></form>';

print <<EOF;
<vxml version="2.1">
$selection
$detail
</vxml>
EOF

The VXML generated is shown in Listing 10. You can see from the output how stories are listed, how the opportunity is provided for input, and how help is given to the listeners so they know what to do.


Listing 10. Offering news selection through VXML
                
<vxml version="2.1">
  <form id="MainMenu">
    <field name="select_num" type="digits">
      <prompt>
        BBC News | UK | UK Edition
        <break size="small" />
      </prompt>
      <prompt>
        Please select a story from the following list.
      </prompt>
      <prompt>
        1: Prince Harry not to serve in Iraq
        <break size="small" />
      </prompt>
      <prompt>
        2: Madeleine fighting fund launched
        <break size="small" />
      </prompt>
      <prompt>
        3: Salmond elected as first minister
        <break size="small" />
      </prompt>
      <prompt>
        4: Sainsbury profits jump to £380m
        <break size="small" />
      </prompt>
      <prompt>
        5: Police boo John Reid over pay
        <break size="small" />
      </prompt>
      <prompt>
        6: Uncle jailed for owning death dog
        <break size="small" />
      </prompt>
      <noinput>
        Please select a number.
        <reprompt />
      </noinput>
      <nomatch>
        Please select a valid number.
        <reprompt />
      </nomatch>
    </field>
    <filled>
      <assign name="selection" expr="select_num" />
      <if cond="selection =='1'">
        <prompt>
          Prince Harry not to serve in Iraq. Prince Harry will
          not be deployed in Iraq because of the security
          threat, the head of the Army says.
          <break size="small" />
        </prompt>
        <elseif cond="selection =='2'" />
        <prompt>
          Madeleine fighting fund launched. A fighting fund is
          launched to help cover escalating costs in the
          search for missing Madeleine McCann.
          <break size="small" />
        </prompt>
        <elseif cond="selection =='3'" />
        <prompt>
          Salmond elected as first minister. Alex Salmond
          makes history after becoming the first Nationalist
          to be elected first minister of Scotland.
          <break size="small" />
        </prompt>
        <elseif cond="selection =='4'" />
        <prompt>
          Sainsbury profits jump to £380m. Sainsbury's, the
          supermarket chain that was the target of takeover
          speculation, sees its full-year profits surge.
          <break size="small" />
        </prompt>
        <elseif cond="selection =='5'" />
        <prompt>
          Police boo John Reid over pay. Home Secretary John
          Reid is booed over pay proposals at the Police
          Federation's annual conference.
          <break size="small" />
        </prompt>
        <elseif cond="selection =='6'" />
        <prompt>
          Uncle jailed for owning death dog. The uncle of a
          five-year-old girl killed by a pit bull terrier is
          jailed for eight weeks for owning an illegal dog.
          <break size="small" />
        </prompt>
      </if>
      <clear namelist="select_num" />
      <reprompt />
    </filled>
  </form>
</vxml>

If you upload this to your VoiceXML browser, you can listen to the stories and select the one you want to hear. The last block after the detailed list of stories provides the opportunity to select another item (see Listing 11).


Listing 11. Select another item
                
<clear namelist="select_num" />
<reprompt />

You have to clear the variable, otherwise the variable is filled with the last input value and further selections will be invalid.

With both the previous selections, you dynamically download the RSS, but still output a static file. For true interactivity, you need to generate this information on the fly.


Creating your RSS VoiceXML using Java servlets

So far the selections were based on providing information in a static VXML file. For a more interactive element, you need to generate the information dynamically through a script. Most voice browser services access the VXML file by reading from a URL, so you need to develop a CGI or other dynamic Web script to create the information.

You can do that with the previous two Perl examples (Listings 7 and 9) by ensuring that you output the correct HTTP header (text/xml) before the VXML is generated. You can do that by just adding two elements to the top of the script (see Listing 12).


Listing 12. Adding two elements to the top of the script
                
use CGI qw/:standard/;
print header(-type => 'text/xml');

That imports the CGI library and then sends the correct HTTP header information; the rest of the output is the same VXML that was generated before.

The same principles apply to other languages. Listing 13 shows a Java servlet that provides the same output as your first Perl example (Listing 7). This one uses the Rome and JDOM libraries to parse the RSS feed before outputting the required VXML.


Listing 13. Generating RSS news feeds through VXML and JSP
                
import java.net.URL;
import java.util.Iterator;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.*;
import javax.servlet.http.*;
import com.sun.syndication.feed.module.Module;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

public class VXMLRSS extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        BufferedInputStream bis = null;
        PrintWriter out = null;

        try {
            out = res.getWriter();
            res.setContentType("text/xml");

            printHeader(out);
            printNews(out);
            printFooter(out);

        } finally {
            if (out != null) out.close();
            if (bis != null) bis.close();
        }
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        doGet(req, res);
    }

    private void printHeader(PrintWriter out) throws IOException {
        out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        out.println("<vxml version=\"2.1\">");
    }

    private void printNews(PrintWriter out) throws IOException {
        try {
            final URL feedUrl = new 
URL("http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml");

            final SyndFeedInput input = new SyndFeedInput();
            final SyndFeed feed = input.build(new XmlReader(feedUrl));

            out.println("<form><block>" + 
                        "<prompt>" + 
                        feed.getTitle() + 
                        "<break size=\"small\"/></prompt>");

            for (final Iterator iter = feed.getEntries().iterator();
                 iter.hasNext();)
                {
                    out.println("<prompt>" +
                                ((SyndEntry)iter.next()).getTitle() + 
                                "<break size=\"small\"/></prompt>");
                }
            out.println("</block></form>");
        }

        catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("ERROR: " + ex.getMessage());
        }
    }

    private void printFooter(PrintWriter out) throws IOException {
        out.println("</vxml>");
    }
}

The basic operation of the feed parser and the generation of the VXML is the same as with the Perl examples. You just fetch the feed and generate the output on the fly, rather than create a static VXML file.

You can install this (and the necessary JDOM and Rome JAR files) into a JSP server environment such as Apache Tomcat and then use this as the basis for your VXML application.


Dynamically generating different VXML

The final part of the process is to provide a list of news feeds that the caller can select to hear, and this also gives you the opportunity to build in the final level of interactivity: calling another script or location when a particular item is selected.

The process starts with a list of available RSS feeds. An array is used in the example, but this information might load from a database. You parse each feed to get the title of the feed and then, as before, output a list of the available feeds and expect an input number. However, this time when a number is selected, you call an external URL, actually, in this case, a call back to the same script, supplying the number of the news feed as an argument. You then call the function that outputs the news feed specific items and lets you select them.

Listing 14 shows the full code, in Perl, for the CGI script that will handle all of this.


Listing 14. The CGI script
                
#!/usr/bin/perl

use XML::FeedPP;

use CGI qw/:standard/;

print header(-type => 'text/xml');

my ($feeds) = ['http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/uk/rss.xml',
               'http://mcslp.com/wp-rss2.php'];


if (param('selection') =~ m/[0-9]+/)
{
    output_news_feed($feeds->[param('selection')]);
}
else
{
    output_feed_list();
}

sub output_feed_list
{
    my ($selection,$detail,$counter) = ('','',0);

    $selection = '<form id="MainMenu"><field name="select_num" 
                                                           type="digits">';
    $selection .= '<prompt>MCSLP News Feed 
                             Reader<break size="small"/></prompt>';
    $selection .= '<prompt>Please select a news source from the following 
                                                              list.</prompt>';

    foreach my $feedurl (@{$feeds})
    {
        my $feed = XML::FeedPP->new($feedurl);

        $counter++;

        if ($counter == 1)
        {
            $detail .= '<filled><assign name="selection" 
                                                       expr="select_num"/>';
            $detail .= "<if cond=\"selection =='$counter'\">";
        }
        else
        {
            $detail .= "<elseif cond=\"selection =='$counter'\"/>";
        }
        $detail .= sprintf('<goto next="http://www.mcslp.com/
                                             rsstovxmlopt.cgi?selection=%s"/>',
                           $counter);

        $selection .= sprintf('<prompt>%d: %s<break 
size="small"/></prompt>',$counter,$feed->title());
    }

    $selection .= '<noinput>Please select a number. 
                                          <reprompt/></noinput>';
    $selection .= '<nomatch>Please select a valid number. 
                                          <reprompt/></nomatch>';
    $selection .= '</field>';

    $detail .= '</if><clear 
namelist="select_num"/><reprompt/></filled></form>';

    print <<EOF;
<?xml version="1.0" encoding="UTF-8"?>

<vxml version="2.1">
$selection
$detail
</vxml>
EOF

}

sub output_news_feed
{
    my ($feedurl) = @_;

    my $feed = XML::FeedPP->new($feedurl);

    my ($selection,$detail,$counter) = ('','',0);

    $selection = '<form id="MainMenu"><field name="select_num" 
                                                              type="digits">';
    $selection .= '<prompt>' . $feed->title() . 
                                     '<break size="small"/></prompt>';
    $selection .= '<prompt>Please select a story from the following 
                                                            list.</prompt>';

    foreach my $i ( $feed->get_item() )
    {
        next unless defined($i);
        next unless ($i->link() =~ m/http/);

        last if ($counter++ >= 6);

        if ($counter == 1)
        {
            $detail .= '<filled><assign name="selection" 
                                                          expr="select_num"/>';
            $detail .= "<if cond=\"selection =='$counter'\">";
        }
        else
        {
            $detail .= "<elseif cond=\"selection =='$counter'\"/>";
        }
        $detail .= sprintf('<prompt>%s. %s<break 
size="small"/></prompt>',$i->title(),$i->description());
        
        $selection .= sprintf('<prompt>%d: %s<break 
size="small"/></prompt>',$counter,$i->title());
    }

    $selection .= '<noinput>Please select a number. 
                                           <reprompt/></noinput>';
    $selection .= '<nomatch>Please select a valid number. 
                                           <reprompt/></nomatch>';
    $selection .= '</field>';

    $detail .= '</if><clear 
namelist="select_num"/><reprompt/></filled></form>';

    print <<EOF;
<?xml version="1.0" encoding="UTF-8"?>

<vxml version="2.1">
$selection
$detail
</vxml>
EOF
}

The script works because the feed number used to provide the feed list and the feed number used to select the feed itself will be the same. For a database solution you might want to attach a unique ID to each news feed and use this for selection, but the principle remains the same.

Incidentally, you might produce all of the required output using VXML. VXML supports different form blocks in a single file that can be used to output all of the different solutions. But generating such a large amount of VXML on the fly is probably impractical, whereas running a script to generate the specification information on the fly is comparatively low volume.

With the dynamic solution, be careful that the generation of the information does not slow the process. For example, in your script, you accessed every feed in the main list, which can be a time consuming prospect. With a database driven solution you could capture the title of the feed and output the information much faster.

Also be aware that with dynamic solution you might need some additional error handling in the event that something goes wrong. A failure to read a news feed in your example produces an empty list of news. Adding a simple prompt that says "Sorry this service is unavailable" if the feed is not processed correctly is helpful to the user in the event of such a failure.


Summary

In this article, you built a number of different solutions to generate the VXML required to listen to an RSS feed. You started with a simple XSL transform and then moved to more advanced Perl- and Java-based solutions to generate the output.

The scripts themselves are relatively basic; the power is in the VXML that is generated in the process, and in the voice browser that uses the VXML to provide the voice-based interface. You saw how you can use the interactivity in the VXML and dynamic scripts together to produce quite complicated voice-based applications with relative ease.

Stay tuned for Part 2, which will look at developing a voice-enabled calendar.



Download

DescriptionNameSizeDownload method
Part 1 sample codex-voicexml1-RSS.zip6KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

  • Rome RSS/Atom syndication: Download these open source Java tools and libraries for parsing, generating and publishing RSS and Atom feeds.

  • XML::FeedPP: Get this module from Perl's module repository, CPAN.

  • JDOM library: Download this DOM-based XML parser for Java programming. It is required by the Rome RSS library.

  • Voxeo: Find a wealth of information and a hosting solution for VoiceXML applications that provides access through traditional, VoIP and Skype.

  • IBM trial software: Build your next development project with trial software available for download directly from developerWorks.

Discuss

About the author

Photo of Martin Brown

Martin Brown has been a professional writer for over eight years. He is the author of numerous books and articles across a range of topics. His expertise spans myriad development languages and platforms -- Perl, Python, Java, JavaScript, Basic, Pascal, Modula-2, C, C++, Rebol, Gawk, Shellscript, Windows, Solaris, Linux, BeOS, Mac OS/X and more -- as well as Web programming, systems management and integration. Martin is a regular contributor to ServerWatch.com, LinuxToday.com and IBM developerWorks, and a regular blogger at Computerworld, The Apple Blog and other sites, as well as a Subject Matter Expert (SME) for Microsoft. He can be contacted through his Web site at http://www.mcslp.com.

Comments



Trademarks

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=248392
ArticleTitle=Voice enabling XML, Part 1: Develop a voice-enabled RSS reader
publish-date=08212007
author1-email=mc@mcslp.com
author1-email-cc=dwxed@us.ibm.com