 |  |
|
난이도 : 중급 Martin Brown, Developer and writer, Freelance
2007 년 10 월 09 일 온라인으로 데이터를 제공하는 쉬운 방식을 제공하는 RSS는 요즘 많은 관심을 받고 있습니다. VoiceXML 애플리케이션 개발과 관련한 네 편의 기술자료 시리즈의 첫 번째 기술자료에서는 음성 실행(voice-enabled) RSS 리더를 개발하는 방법을 설명합니다. 애플리케이션에 대한 인풋은 RSS 데이터가 되고, 아웃풋은 호환되는 음성 애플리케이션에 의해 읽고 말할 수 있는 VoiceXML이 됩니다.
머리말
음성 실행 RSS 리더를 활용하는 것에 관심 있는 사람이라면 이 글이 도움이 될 것이다. VoiceXML 기초와 RSS XML 포맷을 비롯하여 다음 내용들을 배우게 될 것이다.
- XSLT를 사용하여 RSS를 VXML로 변환하기
- Perl 스크립트를 작성하여 VXML 생성하기
- VXML 파일에 상호 작동성(interactivity) 추가하기
- 자바™ 서블릿을 사용하여 VXML 생성하기
시리즈 소개
음성과 오디오는 웹에서 점점 더 대중화 되고 있다. 현재 온라인에서 사용할 수 있는 음악이나 웹 캐스트(webcast)가 한 예이다. 본 시리즈에서는 음성과 XML을 결합하여 유용한 애플리케이션들을 개발하는 여러 가지 방법을 소개하고자 한다.
- Part 1—음성 실행 RSS 리더
-
Part 2
—음성 실행 달력
-
Part 3
—음성 실행 블로그 및 Twitter 애플리케이션
-
Part 4
—음성 실행 Yahoo 검색 애플리케이션
VoiceXML 기초
VoiceXML은 음성 기반 XML 아웃풋 표준에 주어진 이름이다. 파일 포맷 자체는 VXML이라고 한다.
VXML은 VoiceXML 콘텐트를 텍스트[Text-To-Speech (TTS)]로 변환하는 VoiceXML 브라우저와 결합될 때 더욱 강력해지며, 음성 명령어를 인식할 수도 있다. (음성 인식)
Listing 1은 VXML 파일용 기본 포맷이다.
Listing 1. VXML 파일용 기본 포맷
<?xml version="1.0"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.0">
...
</vxml>
|
폼 엘리먼트를 삽입하여 정보 선택을 도입하고 이를 블록으로 나눈다. TTS는 <prompt> 태그 내에 삽입된다. VXML을 사용하여 간단한 말하기(spoken) 문장을 만들 수 있다.
Listing 2. 샘플 VXML 파일
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>
I could ask you anything!
</prompt>
</block>
</form>
</vxml>
|
응답을 기대하는 프롬프트는 음성 인식 시스템에 의해 받아들여져서 인식되는 유효한 단어 리스트를 제공하거나, 전화 키패드를 통해 인풋을 요청할 수 있다. 실제 정보는 변수에 저장되고 if/else 조건 문을 사용하여 다양한 응답들을 만들어 낼 수 있다.
이 글 나머지 부분과 앞으로의 시리즈 글에서는 이렇게 다양한 메소드를 사용하는 방법을 설명할 것이다.
이 글의 예제를 사용하려면, VXML 파일들을 제공할 수 있는 웹 호스팅 서비스와, TTS와 VR 컴포넌트를 전화선의 형태를 통해서 제공하는 VXML 브라우저로 액세스 할 수 있어야 한다. Voxeo는 여러분이 필요로 하는 음성 브라우저 서비스를 제공하고 유선 전화(land line)를 통해서, Session Initiation Protocol (SIP)에 기반한 VoIP 서비스와 Skype를 사용하여 애플리케이션에 다이얼을 걸 수 있다. (참고자료)
RSS 소스 포맷 이해하기
Really Simple Syndication (RSS)는 블로그와 기타 사이트들에 사용되는 정보를 공개하는데 사용되는 XML 기반 솔루션이다. RSS 포맷으로 아티클이나 정보의 리스트를 생성할 수 있고, 많은 RSS 피드들의 소스들을 결합할 수 있다. (애그리게이션). 그 결과, URL(아티클 원문), 요약 정보, 각 아이템의 구분 정보를 가진 아티클 또는 스토리의 리스트가 생긴다. RSS 피드 전체적으로 더욱 구분되고 세분화 된 데이터를 갖고 있다.
RSS는 XML로 된 많은 신디케이션 포맷들 중 하나일 뿐이다. RSS 파일에서 기본 구조는 매우 단순하다. 처음에는 따라가기 어려워 보이지만, 단순화 된 구조는 Listing 3과 같다.
Listing 3. RSS 구조
<?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>
|
이 헤더 콘텐트는 피드 내에서 정보를 기술하고 있다. —이 경우, 필자의 블로그에 MCslp라는 타이틀을, "News from the desk of Martin MC Brown"이라는 디스크립션이 있다는 것을 알 수 있다.
Listing 3의 나머지 부분은 공개 날짜(블로그 같은 동적 피드의 경우 RSS XML이 생성된 때이다.)와 개별 아이템 블록 같은 피드용 추가 정보를 제공한다.
각 아이템 블록에는 타이틀, 요약 정보, RSS 피드가 지원하는 상세 (또는 전체) 내용들이 있다. Listing 4는 각 아이템 포맷 모습이다.
Listing 4. XML로 된 RSS 아이템
<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>
|
VXML 버전의 정보의 경우, 이러한 엘리먼트 일부를 무시할 수도 있다. 음성 버전의 정보로서 무엇인가를 읽을 때 여러분이 수행할 수 있는 것에는 한계가 있다. —예제 범주는 무의미하고, guid와 기타 엘리먼트는 내부 사용 전용이다.
이 정보에서 실제로 중요한 부분들은 전체 피드용 타이틀, 각 아이템의 타이틀, 각 아이템의 디스크립션 또는 콘텐트이다.
RSS 파일을 VoiceXML로 변환하는 매우 간단한 방법을 알아보자.
XSL 변형 사용하기
기본 RSS XML 파일을 VXML로 변환하는 가장 빠르고 간단한 방법 중 하나는 XSL 템플릿을 사용하여 RSS XML을 필수 아웃풋을 변환하는 것이다. 이 프로세스가 가진 한계는 특정 연산이 매우 어렵다는 점이다. XSL 내에서 엘리먼트를 선택하고 결정을 내리는데 사용되는 메소드가 제한되고 얽혀있기 때문이지만, 빠르고 좋은 솔루션인 것 만은 확실하다.
Listing 5는 RSS 소스 파일 포맷의 타이틀과 디스크립션 엘리먼트를 VXML로 변환하여 RSS 피드 내에서 각 아이템의 헤드라인과 콘텐트를 읽을 수 있게 하는 부분이다.
Listing 5. RSS를 VXML로 변환하는 XSL 스타일시트
<?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>
|
XSL 스타일시트는 XPath 정의를 사용하여 RSS 내에서 모든 아이템들을 처리하여 아이템들을 고른다. 그리고 나서, 타이틀과 디스크립션의 값을 프롬프트 블록에 삽입하면서, break 블록을 사용하여 이 두 개를 분리한다. VoiceXML 내에서, break 태그는 TTS 아웃풋에 중지를 가져온다.
이 스타일시트를 RSS XML(아래 예제는 BBC 뉴스 웹 사이트에서 발췌한 것임) 문서를 통해 xsltproc을 사용하여 실행할 수 있다. (Listing 6)
Listing 6. XSL 스타일시트로 VXML 생성하기
$ 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>
|
여러분도 보다시피, 각 아이템에 블록, 타이틀, 중지, 요약 콘텐트를 만들어 낸다. 생성된 VXML을 브라우저로 업로드 하고 아웃풋을 리스닝 한다. 결과는 그렇게 현실적인 것은 아니다. 다운로드 솔루션을 구가하고 주기적으로 파일을 자동 생성해야 하기 때문이다. 따라서, 여러분은 필요한 정보를 보다 동적으로 만들어 내는 방법에 대해 생각해야 한다.
RSS에서 VoiceXML 워크플로우
XSL 스타일시트 프로세스가 가진 한 가지 문제는 매우 단순하면서도 매우 제한적이라는 점이다. VXML 파일을 생성하는 옵션과 생성된 VXML에서 이러한 한계가 드러난다. 이를 강화하기 위해, Perl 또는 자바를 사용하여 변환을 수행할 수 있는데, 생성 프로세스가 조금 더 복잡해 진다.
이를 수행하기 전에, RSS 브라우저가 작동하는 방식을 구상해 보자. 그림 1의 프로세스 예제를 보자.
그림 1. RSS에서 VXML 애플리케이션으로의 워크플로우
기본적으로, 숫자가 호출되면, 콜러에게 가용 RSS 피드 리스트를 제공한다. 사용자가 하나의 피드를 선택하면, 피드 콘텐트를 처리하고 그 피드에 각 아이템을 위한 헤드라인들을 리스팅 한다. 콜러는 개별 뉴스 스토리를 선택하여 듣는다.
시작하려면, 피드를 처리하고 VXML을 출력하여 스토리를 읽어서 메인 정보 리스트를 생성한다.
Perl을 사용하여 RSS에서 VoiceXML 생성하기
Perl은 신디케이션 피드를 처리하는데 사용할 수 있는 다양한 모듈을 많이 갖고 있지만, 현재로서는 가장 유용한 것이 XML::FeedPP 모듈이다. 이것은 어떤 신디케이션 피드라도 처리하며(메인 RSS, RDF, Atom 포맷), 단순화 된 인터페이스를 통해 콘텐트를 제공한다.
피드를 파싱하려면, 새로운 XML::FeedPP 객체를 생성할 때 피드 URL을 제공한다. my $feed = XML::FeedPP->new($feedurl);.
피드에 대한 타이틀과 기타 정보는 탑 레벨 객체로부터 액세스 될 수 있다. 예를 들어, 피드 타이틀에 액세스 하는 방법은 $feed->title();이다.
이 피드 내에서 개별 아이템들에 액세스 하려면, get_item() 메소드를 사용하여 아이템 객체들의 리스트를 얻고, 더 자세한 메소드를 사용하여(title() 또는 description()) 각 아이템의 실제 콘텐트에 액세스 한다.
Listing 7의 코드를 사용하여 주어진 피드에서 뉴스 아이템 리스트를 만들어 낼 수 있다.
Listing 7. Perl을 사용하여 RSS 피드에서 VXML 파일에 액세스 하기
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
|
Listing 7의 스크립트는 두 개의 주 요소가 있다. 첫 번째 부분은 피드에 액세스 하여 각 뉴스 아이템 타이틀에 대한 프롬프트 블록을 만들어 낸다. 이는 순서대로 붙어서 하나의 스트링을 만든다. 두 번째 부분은 전체 스트링을 VXML 파일의 핵심으로 삽입하는 것이다.
각 아이템에, 아이템 리스트를 반복하고, 타이틀을 출력하면, 잠시 동안의 오디오 중지가 이어진다. 성능이 좋은 XML 제너레이터를 사용하여 이 정보를 출력할 수 있지만, XML의 생성은 비교적 간단하고, 표준 print 문을 사용하여 정보를 만들어 낼 수 있다.
RSS 피드는 하드 코딩 되었고 표준 아웃풋으로 생성되었기 때문에, 파일로 된 것을 원한다면 이 아웃풋을 파일로 리다이렉트 해야 한다. 결과 VXML은 Listing 8과 같다.
Listing 8. RSS 피드에서 자동 생성된 VXML
<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>
|
이를 VoiceXML 호환 서비스로 업로드 하고 다이얼을 걸면, 피드에서 직접 뉴스 기사 리스트를 들을 수 있다.
이 예제를 사용하여, 헤드라인에만 접근한다. 좋은 점은 헤드라인을 듣고 뉴스 아이템의 요약을 들을 옵션이 있다는 점이다.
스토리 선택 및 상세 추가하기
옵션과 요약 기능을 추가하려면, VXML을 생성할 때 몇 가지를 수행해야 한다.
- 옵션 리스트(리스트 앞 또는 뒤에 숫자가 있어야 한다.)와 리스닝 번호를 수락하는 기능을 생성해야 한다.
- 인커밍 숫자를 파싱할 수 있어야 한다. 음성 단독으로 이를 수행할 수 있지만, 이 예제의 경우, DTMF 인풋을 허용한다.
- 전체 스토리 아웃풋을 제공해야 한다.
이 리스트의 첫 번째 아이템은 단순하다.—고유 숫자를 캡쳐하고 그 숫자와 스토리 헤더를 출력하면 된다. 숫자가 선택될 때 스토리 헤더(그리고 숫자)가 전체 요약과 매치되는지 확인하려면, 두 개의 임시 스트링을 사용한다. 하나에는 가능한 선택이 포함되어 있고, 다른 것에는 상세한 아웃풋이 포함되어 있다.
키패드에서 숫자를 캡쳐하려면, VXML 내에서 인풋을 캡쳐 할 필드 스팩을 사용한다: <field name="select_num" type="digits">.
이제 시스템은 사용자가 숫자들을 입력할 때까지 기다린다. 변수가 채워지면 VXML에 채워진 블록이 파싱된다. 이는 출력할 뉴스 요약 정보를 선택하고 음성화 될 텍스트 블록을 출력하는데 사용될 것이다.
값을 파싱하려면, Dual Tone Multi-Frequency (DTMF) 톤에서 입력된 숫자를 캡쳐하고 나서 이것을 하나의 변수 안에 둔다: <assign name="selection" expr="select_num"/>.
여기에서는 설명할 목적으로 6 개의 탑 스토리만 출력하겠지만, 이러한 방식으로 여러 숫자들을 수락할 수 있다.
숫자가 제공되면, if 블록을 사용하여 재생될 알맞은 스토리를 선택한다.
여기 다시 Perl로 작성된 전체 스크립트를 볼 수 있다. (Listing 9)
Listing 9. 전체 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,$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
|
생성된 VXML은 Listing 10에 나타나 있다. 이 아웃풋을 통해서 스토리가 어떻게 리스팅 되는지, 인풋을 위한 기회가 어떻게 제공되는지, 리스너에게 도움말이 어떻게 제공되는지를 알 수 있다.
Listing 10. 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>
|
이것을 VoiceXML 브라우저에 업로드 하면, 스토리를 듣고 원하는 것 하나를 선택할 수 있다. 스토리의 상세 리스트 다음에 있는 마지막 블록은 또 다른 아이템을 선택할 수 있는 기회이다. (Listing 11)
Listing 11. 다른 아이템 선택하기
<clear namelist="select_num" />
<reprompt />
|
변수를 지워야 한다. 그렇지 않으면, 이 변수는 마지막 인풋 값으로 채워지고 그 이후의 선택들은 무효가 된다.
이전 선택들을 사용하여 RSS를 동적으로 다운로드 하지만, 아웃풋은 여전히 정적인 파일이다. 진정한 상호 작동성을 위해서, 이 정보를 즉각적으로 생성해야 한다.
자바 서블릿을 사용하여 RSS VoiceXML 생성하기
지금까지 선택은 정적인 VXML 파일로 정보를 제공하는 것에 기반했다. 보다 대화적인(interactive) 엘리먼트를 위해서는 스크립트를 통해서 정보를 동적으로 생성해야 한다. 대부분의 음성 브라우저 서비스는 URL에서 읽어서 VXML 파일에 액세스 하기 때문에, CGI나 기타 동적 웹 스크립트를 개발하여 정보를 만들어야 한다.
VXML이 생성되기 전에 정확한 HTTP 헤더(text/xml)을 출력할 수 있다는 것을 확인함으로써 두 개의 Perl 예제(Listing 7과 9)를 사용하여 이것을 수행할 수 있다. 스크립트에 두 개의 엘리먼트를 추가해도 이것이 가능하다. (Listing 12).
Listing 12. 스크립트에 두 개의 엘리먼트 추가하기
use CGI qw/:standard/;
print header(-type => 'text/xml');
|
이것은 CGI 라이브러리를 가져와서 정확한 HTTP 헤더 정보를 보낸다. 이 아웃풋의 나머지는 전에 생성되었던 것과 같은 VXML이다.
같은 원리가 다른 언어에도 적용된다. Listing 13은 첫 번째 Perl 예제와 같은 아웃풋을 제공하는 자바 서블릿이다. (Listing 7) 이것은 Rome과 JDOM 라이브러리를 사용하여 필요한 VXML을 출력하기 전에 RSS 피드를 파싱한다.
Listing 13. VXML과 JSP를 통해서 RSS 뉴스 피드 생성하기
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>");
}
}
|
피드 파서의 기본적인 연산과 VXML의 생성은 Perl 예제와 같다. 정적인 VXML 파일을 만드는 대신, 피드를 보내고 동시에 아웃풋을 생성하면 된다.
이것을(그리고 필수 JDOM과 Rome JAR 파일)을 Apache Tomcat 같은 JSP 서버 환경에 설치하고 이를 VXML 애플리케이션의 토대로서 사용한다.
다른 VXML을 동적으로 생성하기
이 프로세스의 마지막 부분은 콜러가 듣기 위해 선택하는 뉴스 피드 리스트를 제공하는 것이다. 이 부분에서도 상호 작동성을 고려해야 한다. 특정 아이템이 선택될 때 또 다른 스크립트나 위치를 호출한다.
이 프로세스는 가용 RSS 피드 리스트로 시작한다. 어레이는 예제에서 사용되지만, 이 정보는 데이터베이스에서 로딩된다. 각 피드를 파싱하여 피드의 타이틀을 얻고, 전과 마찬가지로, 가용 피드의 리스트를 출력하고 인풋 숫자를 예상한다. 하지만, 이번 경우에는, 숫자가 선택될 때, 외부 URL(이 경우, 같은 스크립트에 대한 콜백)을 호출하면서 뉴스 피드의 숫자를 인자로서 제공한다. 그리고 나서, 뉴스 피드 아이템을 출력하고 이를 선택할 수 있게 해주는 함수를 호출한다.
Listing 14는 이 모든 것을 다룬 CGI 스크립트에 대한 전체 Perl 코드이다.
Listing 14. CGI 스크립트
#!/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
}
|
피드를 제공하는데 사용되는 피드 번호와 피드 자체를 선택하는데 사용되는 피드 번호는 같기 때문에 이 스크립트는 작동한다. 데이터베이스 솔루션을 위해, 각 뉴스 피드에 고유 ID를 붙이고 이를 선택에 사용한다. 원리는 똑같다.
때로는, VXML을 사용하여 필요한 모든 아웃풋을 만들 수도 있다. VXML은 모든 다른 솔루션들을 출력하는데 사용될 수 있는 싱글 파일로 된 다양한 폼 블록을 지원한다. 이와 같은 많은 VXML을 즉각적으로 생성하는 것은 비현실적이지만, 스팩 정보를 즉각 생성하는 스크립트를 실행하는 것은 비교적 적은 분량이다.
이러한 동적인 솔루션을 사용할 때, 정보의 생성으로 프로세스가 느려지지 않도록 해야 한다. 예를 들어, 스크립트에서 메인 리스트에 있는 모든 피드에 액세스 하는데, 이것은 시간이 많이 든다. 데이터베이스 중심 솔루션을 사용하여, 피드의 타이틀을 캡쳐할 수 있고 정보를 보다 빠르게 출력할 수 있다.
동적인 솔루션을 사용할 때, 추가적인 에러 핸들링도 필요하다. 이 예제에서 뉴스 피드 읽기가 실패하면 비어있는 뉴스 리스트가 출력된다. 피드가 올바르게 처리되지 않을 경우 "Sorry this service is unavailable" 라는 내용의 프롬프트를 추가하는 것도 오류 발생시 도움이 될 것이다.
요약
이 글에서, RSS 피드를 듣는데 필요한 VXML을 생성하는 많은 솔루션들을 구현해 보았다. 단순한 XSL 변형으로 시작하여 보다 고급의 Perl 및 자바 기반 솔루션을 사용하여 아웃풋을 만들었다.
스크립트 자체는 다분히 기본적이다. 진정한 힘은 프로세스에서 생성된 VXML에 있고 음성 기반 인터페이스를 제공하기 위해 VXML을 사용하는 음성 브라우저에 있다. VXML의 상호 작동성과 동적인 스크립트를 함께 사용하여 매우 복잡한 음성 기반 애플리케이션들을 비교적 쉽게 만들어 내는 방법을 배웠다.
음성 실행 달력을 개발하는 방법을 다룰, Part 2를 기대해 주기 바란다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| Part 1 샘플 코드 | x-voicexml1-RSS.zip | 6KB | HTTP |
|---|
참고자료 교육
제품 및 기술 얻기
토론
필자소개  | 
|  | Martin Brown은 8년 경력의 전문 작가이다. 다양한 주제로 많은 책과 기술 자료을 쓰고 있다. Perl, Python, Java, JavaScript, Basic, Pascal, Modula-2, C, C++, Rebol, Gawk, Shellscript, Windows, Solaris, Linux, BeOS, Mac OS/X 같은 개발 플랫폼과 웹 프로그래밍, 시스템 관리와 통합 분야를 다루고 있다. Martin은 ServerWatch.com, LinuxToday.com, IBM developerWorks에 정기적으로 기고하고 있으며, Computerworld, The Apple Blog, Subject Matter Expert (SME) for Microsoft에 블로그를 운영하고 있다. (http://www.mcslp.com) |
기사에 대한 평가
|  |