| VoiceXML 2.1 Development Guide | Home | Frameset Home |
|
<data> element to fetch our newsfeed, a JavaScript function to format the returned data, and the <foreach> element to loop through the data and present it to the caller. We're also going to use the <link> tag for global user commands and show how to use document-scoped variables in the VoiceXML context.<form> tags to encapsulate these steps:<forms>. In the interest of brevity, we will include some inline notations to explain their use in the application:
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1" xmlns:voxeo="http://community.voxeo.com/xmlns/vxml">
<!-- DECLARE DOCUMENT SCOPED VARIABLES -->
<!-- THIS VARIABLE WILL HOLD THE CALLER'S TEAM CHOICE -->
<var name="teamRSS"/>
<var name="dataRSS"/>
<!-- THIS HOLDS THE NODE REFERENCE FOR OUR DATA WE FETCH FROM THE RSS -->
<var name="descriptionArrayRSS"/>
<!-- THIS VARIABLE WILL BE POPULATED WITH AN ARRAY CONTAINING ALL ITEMS FROM THE RSS FEED-->
<var name="fetchRSS"/>
<!-- THIS IS OUR DATA ELEMENT'S FORM ITEM VARIABLE-->
<var name="resultCount"/>
<form id="Intro">
<block>
<prompt bargein="true">
<audio src="welcome.wav">
Welcome to the real time Basketball news, where you can get up to date
information on the recent happenings for your favorite En Bee Eh team.
</audio>
</prompt>
<goto next="#Team"/>
</block>
</form>
<form id="Team">
<field name="chooseTeam" modal="true">
<grammar src="TeamGram.php" type="application/srgs+xml"/>
<prompt baregin="true">
<audio src="chooseTeam.wav">
To begin, please choose the name of the En Bee Eh team you would like to get news for.
</audio>
</prompt>
<filled>
<!-- ASSIGN THE CALLER'S TEAM NAME TO A DOCUMENT SCOPED VARIABLE -->
<log expr="'***** USER INPUT FOR CHOOSETEAM = ' + lastresult$.interpretation.teamRSS"/>
<assign name="document.teamRSS" expr="lastresult$.interpretation.teamRSS"/>
</filled>
</field>
<field name="confirmTeam" type="boolean" modal="true">
<prompt bargein="false">
<audio src="youChose.wav">
you chose the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
</audio>
<audio src="isCorrect.wav">
is this correct?
</audio>
</prompt>
<filled>
<log expr="'***** USER INPUT FOR CONFIRMTEAM = ' + lastresult$.interpretation.confirmTeam"/>
<if cond="confirmTeam == true">
<!-- IF THE INPUT WAS RECOGNIZED CORRECTLY, FETCH THE TEAM RSS FEED -->
<goto next="#NBAHeadlines"/>
<else/>
<!-- IF THE INPUT WAS NOT RECOGNIZED CORRECTLY, START THE DIALOG OVER -->
<clear namelist="chooseTeam confirmTeam"/>
<goto nextitem="chooseTeam"/>
</if>
</filled>
</field>
</form>
<form id="NBAHeadlines">
<block name="fetchData">
<!-- WE USE THE DOCUMENT SCOPED VARIABLE TO CHOOSE WHICH RSS FEED WE WILL USE FROM NBA.com -->
<data name="fetchRSS" srcexpr="'http://www.nba.com/'+ document.teamRSS +'/rss.xml'"/>
</block>
</form>
</vxml>
<forms> for our three distinct steps in the application. We added voice recognition dialogs to allow the caller to choose and confirm the NBA team that they want news on, and we prepared a handful of document-scoped variables for holding the data we will eventually fetch from the RSS feed. One of these document-scoped variables will be used to determine which RSS feed that we fetch our data from in the <data srcexpr> element, (document.teamRSS). In our next step, we will flesh out our logic a bit more and explore how we use the <data> tag, add some ECMAScript to grab our third-party RSS information, and format it so that it's elegantly outputted to our caller.<item> element.
<item>
<item repeat="0-1"> team city </item>
<item repeat="0-1"> team name </item>
<tag> teamRSS ='team name' </tag>
</item>
<data> element: This multipurpose addition to the VoiceXML 2.1 specification can be used both to send information from a VoiceXML dialog to an external file and to grab external data from an XML formatted file and bring it into the VoiceXML context. In past tutorials, we have illustrated the usage of this element sending to a file, but in this tutorial, we will show how we grab data from a file, without using any server-side logic at all. In this case, all we need are some basic skills with JavaScript and an understanding of the Document Object Model (DOM, for short). This tutorial really shows off how powerful this element is and opens the door to former functionality that was impossible to implement without the usage of server-side logic. In short, this is possibly the most powerful addition to the VoiceXML 2.1 specification.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="2.0">
<channel>
<title>NBA.com: Magic News</title>
<link>http://www.nba.com/</link>
<description></description>
<language>en-us</language>
<copyright>Copyright (c) 2006 NBA Media Ventures....</copyright>
<managingEditor>webmaster@nba.com</managingEditor>
<image>
<title>NBA.com</title>
<url>http://stats.surfaid.ihost.com.....</url>
<link>http://www.nba.com/</link>
<width>94</width>
<height>22</height>
<item>
<title><![CDATA[ Article Title 1 ]]></title>
<link>Link to Article 1 on NBA.com</link>
<description><![CDATA[Article Number 1 Body...we can assume that the Magic lost a game]]></description>
</item>
<item>
<title><![CDATA[ Article Title 2 ]]></title>
<link>Link to Article 2 on NBA.com</link>
<description><![CDATA[Article Number 2 Body...we can assume that the Magic lost another game]]></description>
</item>
<!-- ETC.... -->
</channel>
</rss>
<description> nodes at the beginning of the document that we will want to skip over, so we will be taking this into account when we write up that function:
<title>NBA.com: Magic News</title>
...
<description></description>
...
<title>NBA.com</title>
...
// function #1: count the nodes
// this counts how many headlines are available for a team
function getCount(d) {
var x=d.getElementsByTagName("item");
// invoked using "getCount(dataName)"
return x.length;
// returns an integer holding the number of nodes, this gives us an "article count"
}
// function #2: grab the text data from the nodes and append them to an array
function assignArray(d, n, r) {
var j=(d.getElementsByTagName(r).length + 2);
// remember that we need to "skip" over the first nodes named "description"
var NBAarray;
NBAarray = new Array();
// declare an empty array
for(var i = 0; i < j; i++) {
// since its possible that a title or description entry from the RSS feed
// will be empty, or have 'bad' characters, we need to do some error handling
try {
NBAarray[i] = d.getElementsByTagName(n).item(i).firstChild.data;
// loop through the node values and append each entry to our comma-delimited array
}
// if the node value is undefined...
catch(e)
{
//..then assign its value as an empty string to prevent the app from crashing
NBAarray[i] = '';
}
}
return NBAarray;
// return the array holding all values from the node in question
}
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1" xmlns:voxeo="http://community.voxeo.com/xmlns/vxml">
<!-- DECLARE SCRIPT THAT PARSES THROUGH THE XML NEWSFEED DATA -->
<script src="parseRSSData.js" fetchtimeout="15s"/>
<var name="teamRSS"/>
<var name="dataRSS"/>
<var name="descriptionArrayRSS"/>
<var name="fetchRSS"/>
<var name="resultCount"/>
<form id="Intro">
<block>
<prompt bargein="true">
<audio src="welcome.wav">
Welcome to the real time Basketball news, where you can get up to date
information on the recent happenings for your favorite En Bee Eh team.
</audio>
</prompt>
<goto next="#Team"/>
</block>
</form>
<form id="Team">
<field name="chooseTeam" modal="true">
<grammar src="TeamGram.xml" type="application/srgs+xml"/>
<prompt baregin="true">
<audio src="chooseTeam.wav">
To begin, please choose the name of the En Bee Eh team you would like to get news for.
</audio>
</prompt>
<filled>
<log expr="'***** USER INPUT FOR CHOOSETEAM = ' + lastresult$.interpretation.teamRSS"/>
<assign name="document.teamRSS" expr="lastresult$.interpretation.teamRSS"/>
</filled>
</field>
<field name="confirmTeam" type="boolean" modal="true">
<prompt bargein="false">
<audio src="youChose.wav">
you chose the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
</audio>
<audio src="isCorrect.wav">
is this correct?
</audio>
</prompt>
<filled>
<log expr="'***** USER INPUT FOR CONFIRMTEAM = ' + lastresult$.interpretation.confirmTeam"/>
<if cond="confirmTeam == true">
<goto next="#NBAHeadlines"/>
<else/>
<clear namelist="chooseTeam confirmTeam"/>
<goto nextitem="chooseTeam"/>
</if>
</filled>
</field>
</form>
<form id="NBAHeadlines">
<block name="fetchData">
<data name="fetchRSS" srcexpr="'http://www.nba.com/'+ document.teamRSS +'/rss.xml'"/>
<!-- ASSIGN OUR DATA FETCH TO A VOICEXML VARIABLE -->
<assign name="document.dataRSS" expr="fetchRSS.documentElement"/>
<!-- INVOKE OUR RESULT COUNT FUNCTION, AND IMPORT THE VALUE TO A VOICEXML VARIABLE -->
<assign name="document.resultCount" expr="getCount(fetchRSS)"/>
<!-- INVOKE OUR ASSIGNARRAY FUNCTION, AND IMPORT THE ARRAY TO A VOICEXML VARIABLE -->
<assign name="document.descriptionArrayRSS" expr="assignArray(fetchRSS, 'description', 'item')"/>
<!-- CLIP OFF THE FIRST ARRAY VALUE, (TO IGNORE THE FIRST INSTANCE OF 'DESCRIPTION' -->
<assign name="document.descriptionArrayRSS" expr="document.descriptionArrayRSS.slice(1, -1);"/>
<prompt>
<audio src="weHave.wav">
We have
</audio>
<audio expr="document.resultCount + '.wav'">
<value expr="document.resultCount"/>
</audio>
<audio src="artAvail.wav">
articles available for the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
<break size="small"/>
</audio>
</prompt>
</block>
</form>
</vxml>
<script> element), and the invocation of the function calls (the <var> assignations just below the <data> element). Now, all that remains to is to output our array values to the caller and add a final bit of polish to our menu structure.<foreach> element. This element is probably familiar to our advanced developers out there; a "foreach" loop structure is used in a number of client-side and server-side scripts. The inclusion of this element in the 2.1 specification allows developers an easy way of looping through a comma-delimited list of array values and outputting them sequentially via TTS, <log> statements, and the like. To further illustrate, let's look at some dummy data in a true array format:
<!-- "myArray" -->
[value 1, value 2, value 3, value 4]
<foreach> element, we can then output these values one by one to our callers and add a pause between each "loop" of the values:
<foreach item="arrayItem" array="myArray">
<prompt>
<value expr="arrayItem"/>
<break size="medium"/>
</prompt>
</foreach>
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1" xmlns:voxeo="http://community.voxeo.com/xmlns/vxml">
<script src="parseRSSData.js" fetctimeout="15s"/>
<var name="teamRSS"/>
<var name="dataRSS"/>
<var name="descriptionArrayRSS"/>
<var name="fetchRSS"/>
<var name="resultCount"/>
<!-- DECLARE OUR DOCUMENT SCOPED LINK GRAMMAR -->
<link next="#Team">
<grammar root="main">
<rule id="main" scope="public">
<one-of>
<item> start over </item>
<item> main menu </item>
<item> main </item>
</one-of>
</rule>
</grammar>
</link>
<form id="Intro">
<block>
<prompt bargein="true">
<audio src="welcome.wav">
Welcome to the real time Basketball news, where you can get up to date
information on the recent happenings for your favorite En Bee Eh team.
</audio>
</prompt>
<goto next="#Team"/>
</block>
</form>
<form id="Team">
<field name="chooseTeam" modal="true">
<grammar src="TeamGram.xml" type="application/srgs+xml"/>
<prompt baregin="true">
<audio src="chooseTeam.wav">
To begin, please choose the name of the En Bee Eh team you would like to get news for.
</audio>
</prompt>
<filled>
<log expr="'***** USER INPUT FOR CHOOSETEAM = ' + lastresult$.interpretation.teamRSS"/>
<assign name="document.teamRSS" expr="lastresult$.interpretation.teamRSS"/>
</filled>
</field>
<field name="confirmTeam" type="boolean" modal="true">
<prompt bargein="false">
<audio src="youChose.wav">
you chose the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
</audio>
<audio src="isCorrect.wav">
is this correct?
</audio>
</prompt>
<filled>
<log expr="'***** USER INPUT FOR CONFIRMTEAM = ' + lastresult$.interpretation.confirmTeam"/>
<if cond="confirmTeam == true">
<goto next="#NBAHeadlines"/>
<else/>
<clear namelist="chooseTeam confirmTeam"/>
<goto nextitem="chooseTeam"/>
</if>
</filled>
</field>
</form>
<form id="NBAHeadlines">
<block name="fetchData">
<data name="fetchRSS" srcexpr="'http://www.nba.com/'+ document.teamRSS +'/rss.xml'"/>
<assign name="document.dataRSS" expr="fetchRSS.documentElement"/>
<assign name="document.resultCount" expr="getCount(fetchRSS)"/>
<assign name="document.descriptionArrayRSS" expr="assignArray(fetchRSS, 'description', 'item')"/>
<assign name="document.descriptionArrayRSS" expr="document.descriptionArrayRSS.slice(1, -1);"/>
<prompt>
<audio src="weHave.wav">
We have
</audio>
<audio expr="document.resultCount + '.wav'">
<value expr="document.resultCount"/>
</audio>
<audio src="artAvail.wav">
articles available for the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
<break size="small"/>
</audio>
<!-- ADD PROMPT THAT INSTRUCTS CALLER ABOUT THE GLOBAL COMMAND -->
<audio src="content.wav">
all content courtesy of En Bee Eh dot com.
To choose another team, you can say main menu at any time.
</audio>
<break size="medium"/>
</prompt>
<!-- SPECIFY OUR DOCUMENT SCOPED VAR AS THE ARRAY VALUE -->
<foreach item="article" array="document.descriptionArrayRSS">
<prompt>
<!-- THE 'ARTICLE' ATTRIBUTE HOLDS THE ARRAY VALUE -->
<!-- CORRESPONDING TO THE CURRENT ITERATION OF THE LOOP -->
<value expr="article"/>
<break size="medium"/>
</prompt>
</foreach>
<prompt bargein="true">
<audio src="noMore.wav">
There are no more news items available for the
</audio>
<audio expr="document.teamRSS + '.wav'">
<value expr="document.teamRSS"/>
<break size="small"/>
</audio>
<audio src="moreInfo.wav">
For more information on your favorite teams, please visit h t t p w w w dot En Bee Eh dot com
<break size="small"/>
</audio>
<audio src="returnToMain.wav">
We will now return you to the main menu, where you can get up to date news items for another En Bee Eh team.
Or, you may hang up, if you are finished.
</audio>
</prompt>
<!-- NAVIGATE TO DUMMY FIELD -->
<goto nextitem="Dummy"/>
</block>
<field name="Dummy" type="boolean">
<!-- MAKE THE FIELD TIMEOUT -->
<property name="timeout" value="0.5s"/>
<!-- CATCH THE TIMEOUT, AND RETURN TO THE START -->
<noinput>
<goto next="NBA_newsreader.xml"/>
</noinput>
<filled/>
</field>
</form>
</vxml>
<link> grammar at the document scope that will allow our callers to say "main menu," (or any of the alternate utterances listed), in order to allow the caller to return to the "choose team" dialog. Within this element, we simply <goto> the <form> where the "choose team" dialog is, and the caller can get news on their second-favorite team.<form id="NBAHeadlines"> does not have an active voice reco <field>, so the recognizer won't be listening for input....for all practical purposes, it is deaf and dumb to our commands of "main menu." However, we can get around this entirely with a "dummy" <field> that is specifically designed to be skipped over, (hence, the "timeout" <property> setting, and the logic within the <nomatch> handler. Pretty slick, eh?<data> element to fetch information from a publicly available RSS feed<foreach> element to loop through array values<link> element allows us to specify a global grammar| ANNOTATIONS: EXISTING POSTS |
carmenati
|
|
| is the rss's url valid?
isn't it www.nba.com/rss/nba_rss.xml instead of www.nba.com/rss.xml?! |
|
MattHenry
|
|
|
Hi there, I don't see where this URL is referenced in our tutorials. Note that within the application itself, we are defining a variable that accessing the correct team name for the RSS feed to be fetched, ie: <data name="fetchRSS" srcexpr="'http://www.nba.com/'+ document.teamRSS +'/rss.xml'"/> ..which could then equate to: http://www.nba.com/magic/rss.xml http://www.nba.com/jazz/rss.xml (etc.) Hope that this helps to clarify! ~Matthew Henry |
|
carmenati
|
|
| hi matt,
now it's clear... tks |
| login |
|