第五節(jié) 文檔的結構和執(zhí)行
|
? |
? |
????VoiceXML文檔主要由一些叫做dialog的上層元素組成。VoiceXML有兩種對話:form和menu。文檔里還可以有<meta>、<metadata>、<var>、<script>、<property>、<catch>和<link>元素。 |
|
一、在一個文檔中執(zhí)行 |
|
????文檔的執(zhí)行由默認的第一個對話開始。每個對話在執(zhí)行的時候,都會指定下一個對話,否則執(zhí)行中止。 |
|
????下面舉個“Hello World”的例子來說明它。這個例子里由有一個文檔級的變量――hi,它的值為字符串“Hello World!”,是第一個form的提示信息。一旦第一個form播放了“Hello World”,控制會跳轉到下一個叫做“say_goodbye”的form,然后播放“Goodbye!”由于第二個form沒有跳轉到其他的對話,文檔退出,執(zhí)行結束。 |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<meta name="author" content="John Doe"/>
<meta name="maintainer" content="hello-support@hi.example.com"/>
<var name="hi" expr="'Hello World!'"/>
<form>
<block>
<value expr="hi"/>
<goto next="#say_goodbye"/>
</block>
</form>
<form id="say_goodbye">
<block>
Goodbye!
</block>
</form>
</vxml>
|
|
????上面的兩個form也可以合起來: |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<meta name="author" content="John Doe"/>
<meta name="maintainer" content="hello-support@hi.example.com"/>
<var name="hi" expr="'Hello World!'"/>
<form>
<block>
<value expr="hi"/> Goodbye!
</block>
</form>
</vxml>
|
|
????元素<vxml>有如下屬性: |
version |
表示該文檔的VoiceXML的版本號(必須的),當前的版本號是2.0。 |
xmlns |
表示給VoiceXML指定的域名空間(必須的)VoiceXML的域名空間是http://www.w3.org/2001/vxml。 |
xml:base |
該文檔的基礎URI,文檔中所有的相對的URI引用都是相對于這個基礎URI的。 |
xml:lang |
表示該文檔的語言標識符,如果省略,默認的語言為平臺特定的語言。 |
application |
表示該文檔的應用根文檔的URI。 |
|
表2:<vxml>元素的屬性 |
|
????在文檔級,語言信息可以通過繼承得到:那些有“xml:lang”屬性的元素可以繼承“xml:lang”的值,例如<grammar>和<prompt>元素,除非這些元素也指定了一個值。 |
? |
二、多文檔應用的執(zhí)行 |
|
????通常,每個文檔都是作為一個孤立的應用運行的。在某些情況下,你想要多個文檔作為一個應用一起運行,你可以選擇一個文檔作為應用根文檔(application root document),其他的作為應用葉文檔(application leaf document),每個葉文檔在它的<vxml>元素里指定根文檔。 |
|
????這樣的話,每次解釋器要加載執(zhí)行該應用的一個葉文檔時,如果應用根文檔還沒有加載,它會先加載應用根文檔。應用根文檔會一直被加載,直到解釋器加載另外一個應用的文檔。因此,解釋器在解釋的時候,下面兩個條件,必須滿足一個: |
|
??????1、應用根文檔已經(jīng)加載,且用戶正在根文檔里執(zhí)行,沒有葉文檔被加載。 |
|
??????2、應用根文檔和一個葉文檔都被加載,且用戶在葉文檔里執(zhí)行。如果各個文檔里有定義了一些subdialog,此時可能不止加載一個葉文檔,但是只能在其中的一個文檔中執(zhí)行。 |
|
????當加載一個葉文檔時,雖然根文檔也被加載,但是根文檔中的對話不會被執(zhí)行,而是在葉文檔中執(zhí)行。 |
|
????多文檔應用有幾個好處: |
|
??????1、葉文檔可以使用根文檔的變量,因此一些信息能夠共享,并保留下來; |
|
??????2、根文檔的<property>元素可以給葉文檔要用的一些property指定默認值; |
|
??????3、公有的ECMAScript代碼可以放在根文檔的<script>元素中,然后在葉文檔中使用; |
|
??????4、根文檔中的<catch>元素可以為葉文檔指定默認的事件處理; |
|
??????5、根文檔中作用域為document的語法在葉文檔中也是激活的,這樣用戶就可以和根文檔中的form、link和menu進行交互。 |
|
????下面是一個有兩個文檔的應用: |
|
????應用根文檔(app-root.vxml): |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<var name="bye" expr="'Ciao'"/>
<link next="operator_xfer.vxml">
<grammar type="application/srgs+xml" root="root" version="1.0">
<rule id="root" scope="public">operator</rule>
</grammar>
</link>
</vxml>
|
|
????葉文檔(leaf.vxml): |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0" application="app-root.vxml">
<form id="say_goodbye">
<field name="answer">
<grammar type="application/srgs+xml" src="/grammars/boolean.grxml"/>
<prompt>Shall we say<value expr="application.bye"/>?</prompt>
<filled>
<if cond="answer">
<exit/>
</if>
<clear namelist="answer"/>
</filled>
</field>
</form>
</vxml>
|
|
????在這個例子中,該應用的入口文件是leaf.vxml,因此leaf.vxml先被加載,它的application屬性指定app-root.vxml為它的應用根文檔,因此接著會加載app-root.vxml。在app-root.vxml中定義了一個變量――bye和一個link,只要用戶說“operator”,那么控制就會跳轉到operator-xfer.vxml。用戶由名為say_goodbye的form開始人機交互。 |
|
C: Shall we say Ciao?
H: Si.
C: I did not understand what you said. (a platform-specific default message.)
C: Shall we say Ciao?
H: Ciao
C: I did not understand what you said.
H: Operator.
C: (Goes to operator_xfer.vxml, which transfers the caller to a human operator.)
|
|
????注意,當用戶在多文檔的應用里,一次最多只有兩個文檔同時加載:應用根文檔和一個應用葉文檔,除非用戶正在和應用根文檔交互(此時只有一個文檔加載,即應用根文檔)。根文檔的元素不能再指定application屬性,而葉文檔一定要指定application屬性。在多文檔的情況下,應用根文檔總是被加載的,而應用葉文檔則不一定。 |
|
????應用根文檔的絕對URI就是解釋器當前的應用名(name of application)。如果有的話,該絕對URI可以包含一個查詢字符串,但是它不可以包含段標識符。只要應用名沒有變,解釋器就一直在同一個應用里。一旦應用名改變,解釋器就進入到一個新的應用里,并初始化新應用的根環(huán)境。應用根環(huán)境包括作用域為application的變量、語法、catch元素、script和property。 |
|
????在用戶會話期間,解釋器從一個文檔到另一個文檔的跳轉可以用<choice>、<goto>、<link>、<subdialog>和<submit>元素。有的跳轉是在某個應用內(nèi),有的是在兩個應用之間。根環(huán)境能否保存取決于跳轉的類型: |
|
??????1、同一應用中根到葉的跳轉 |
|
??????當當前文檔為根文檔,且目標文檔的applocation屬性值和當前應用的絕對URI一樣,此時的跳轉即為同一應用中根到葉的跳轉。該應用根文檔和它的環(huán)境都在跳轉中保留下來。 |
|
??????2、同一應用中葉到葉的跳轉 |
|
??????當當前文檔為葉文檔,且目標文檔的application屬性值和當前應用的絕對URI一樣,此時的跳轉即為同一應用中葉到葉的跳轉,該應用根文檔和它的環(huán)境都在跳轉中保留下來。 |
|
??????3、同一應用中葉到根的跳轉 |
|
??????當當前文檔為葉文檔,且目標文檔的絕對URI和當前應用名一樣,此時的跳轉即為同一應用中葉到根的跳轉。如果是通過<choice>或<goto>或<link>跳轉的,該應用根文檔和它的環(huán)境都在跳轉中保留下來。如果是通過<submit>跳轉的,根環(huán)境會被重新初始化,因為使用<submit>元素總是會導致URI的獲取。 |
|
??????4、根到根的跳轉 |
|
??????當當前文檔為根文檔,且目標文檔也是根文檔,即目標文檔沒有指定application屬性,此時的跳轉就是根到根的跳轉。根環(huán)境和由緩存策略(caching policy)返回的應用根文檔一起被初始化。 即使目標應用名和當前應用名一樣,也可能會使用緩存策略。 |
|
??????5、子對話 |
|
??????當根文檔或葉文檔執(zhí)行<subdialog>元素,此時即為subdialog調(diào)用。正如2.3.4節(jié)所講的,subdialog調(diào)用時,創(chuàng)建了一個新的執(zhí)行環(huán)境。在subdialog執(zhí)行期間,應用根文檔和它的環(huán)境都被保存起來,但是它們在被調(diào)用文檔的執(zhí)行環(huán)境里是不可用的。Subdialog新的執(zhí)行環(huán)境有它自己的根環(huán)境,也許還有葉環(huán)境(leaf context)。當subdialog調(diào)用為一個非空的URI時,就可以用緩存策略來獲得根和葉文檔,以初始化新的根和葉環(huán)境。如果subdialog調(diào)用為一個空的URI,且為段標識符(例如“#sub1”),則根和葉文檔保持不變,因此當前的根和葉文檔會用來初始化新的根和葉環(huán)境。 |
|
??????6、應用之間的跳轉 |
|
??????兩個應用間的跳轉使得應用根環(huán)境和下一個應用的根文檔一起被初始化。 |
|
????如果某個文檔引用了一個不存在的應用根文檔,則拋出一個error.badfetch事件。如果某個文檔的application屬性引用的文檔也指定了application屬性,則拋出一個error.semantic事件。 |
|
????下面的圖表說明了根和葉文檔在應用根環(huán)境中跳轉的結果。在這個圖表中,方框表示文檔,方框內(nèi)紋理的變化表示根環(huán)境的初始化,實心箭頭表示跳轉到箭頭所指向的URI,豎向的虛線箭頭所指向的表示application屬性指定的URI。 |
|
|
圖3: 保存根環(huán)境的的跳轉 |
|
????在這個圖中,所有的文檔都屬于同一應用。1、2、3、4表示跳轉的步驟: |
|
??????1、跳轉經(jīng)由URI A到文檔1,應用環(huán)境被初始化。假設文檔1是這個會話的入口文件。當前的應用名為A。 |
|
??????2、文檔1指定了一個跳轉經(jīng)由URI B到文檔2。文檔2的application屬性值等于A。根文檔為文檔1,和它的環(huán)境一起被保存下來。這是在同一應用中,根到葉的跳轉。 |
|
??????3、文檔2指定了一個跳轉經(jīng)由URI C到另外一個葉文檔,即文檔3。文檔3的application屬性值也等于A。根文檔和它的環(huán)境一起被保存下來。這是在同一應用中,葉到葉的跳轉。 |
|
??????4、文檔3指定了一個跳轉經(jīng)由URI A到文檔1,它是通過或 或來跳轉的。文檔1的根環(huán)境原封不動。這是在同一應用中。葉到根的跳轉。 |
|
????下面的圖說明了初始化根環(huán)境的跳轉。 |
|
|
圖4: 初始化根環(huán)境的跳轉 |
|
??????5、文檔1指定了一個跳轉經(jīng)由URI A到文檔4。文檔4沒有指定它的application屬性,它自己就是根文檔,它的根環(huán)境被初始化。這是一個根到根的跳轉。 |
|
??????6、文檔4指定了一個跳轉經(jīng)由URI D到文檔5。文檔5的application屬性值為URI E和當前的應用名不一樣。解釋器進入到一個新的應用。URI E指向了文檔6。根環(huán)境根據(jù)文檔6的內(nèi)容來初始化。這就是應用之間的跳轉。 |
|
??????7、文檔5指定了一個跳轉經(jīng)由URI A。 |
? |
三、子對話 |
|
????subdialog是一種用來分解復雜的對話序列并改善他們的結構,或創(chuàng)建可重用組件的一種機制。例如帳戶信息請求可能包含了幾部分信息的收集,像帳戶號和家庭電話號碼。這樣的客戶管理服務可以由幾個應用組成 這些應用能夠共享基本的組件,因此用subdialog來構造這個服務就很合理。下面的例子說明了這一點。第一個文檔(app.vxml)用來調(diào)整用戶的帳號,因此,它必須得到帳戶的信息和要調(diào)整的級別。通過subdialog元素調(diào)用另一個VoiceXML文檔讓用戶輸入以獲得帳戶信息。當?shù)诙€文檔執(zhí)行的時候,第一個文檔的dialog被掛起,等待信息的返回。第二個文檔通過<return>元素返回用戶交互的結果,返回值可以通過<subdialog>元素的name屬性定義的變量獲得。 |
|
????用戶服務程序(app.vxml): |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<form id="billing_adjustment">
<var name="account_number"/>
<var name="home_phone"/>
<subdialog name="accountinfo" src="acct_info.vxml#basic">
<filled>
<!-- Note the variable defined by "accountinfo" is
returned as an ECMAScript object and it contain
two properties defined by the variables specified in the
"return" element of the subdialog. -->
<assign name="account_number" expr="accountinfo.acctnum"/>
<assign name="home_phone" expr="accountinfo.acctphone"/>
</filled>
</subdialog>
<field name="adjustment_amount">
<grammar type="application/srgs+xml" src="/grammars/currency.grxml"/>
<prompt>
What is the value of your account adjustment?
</prompt>
<filled>
<submit next="/cgi-bin/updateaccount"/>
</filled>
<field>
<form>
</vxml>
|
|
????包含帳戶信息的subdialog文檔(acct_info.vxml): |
|
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd"
version="2.0">
<form id="basic">
<field name="acctnum">
<grammar type="application/srgs+xml" src="/grammars/digits.grxml"/>
<prompt>What is your account number?</prompt>
<filled>
<field name="acctphone">
<grammar type="application/srgs+xml" src="/grammars/phone_numbers.grxml"/>
<prompt>What is your home telephone number?</prompt>
<filled>
<!-- The values obtained by the two fields are supplied
to the calling dialog by the "return" element. -->
<return namelist="acctnum acctphone"/>
</filled>
</field>
</form>
</vxml>
|
|
????subdialog被調(diào)用的時候產(chǎn)生了一個新的執(zhí)行環(huán)境,subdialog既可以是現(xiàn)有文檔的一個新dialog,也可以是新文檔的一個新dialog。 |
|
????Subdialog可以由幾個文檔組成。圖5展示了一系列文檔(D)跳轉到subdialog(SD),并返回的執(zhí)行流程。 |
|
|
圖5: subdialog由幾個文檔組成,并由最后一個subdialog返回 |
|
????當dialog D2調(diào)用文檔sd2.vxml中的subdialog SD1時,dialog D2的執(zhí)行環(huán)境被掛起,sd1指定執(zhí)行跳轉到sd2.vxml中的dialog(使用<goto>元素)。因此,當sd2.vxml中的dialog返回時,控制直接返回到dialog D2。 |
|
????圖6展示了一個多文檔的subdialog調(diào)用的例子,在這個例子中,控制由一個subdialog跳轉到另一個subdialog。 |
|
|
圖6: 由幾個文檔組成的subdialog,并由第一個subdialog返回 |
|
????在sd1.vxml中的subdialog指定將控制跳轉到sd2.vxml的另一個subdialog,SD2。當執(zhí)行SD2時,總共有兩個環(huán)境被掛起:D2的dialog環(huán)境因等待SD1返回而掛起,SD1的dialog因等待SD2返回而掛起。當SD2返回時,控制也返回到SD1,SD1又把控制返回到dialog D2。 |
? |
四、后期處理 |
|
????在某些情況下(特別是當VoiceXML解釋器處理掛機事件時),解釋器可以繼續(xù)執(zhí)行,進入后期處理狀態(tài),即使解釋器和終端用戶之間的連接已經(jīng)中斷。后期處理的目的是讓VoiceXML應用去完成一些必要的后期的清理。例如提交信息到應用服務器。舉個例子來說,下面的<catch>元素將捕獲connection.disconnect.hangup事件,并進入后期處理狀態(tài)執(zhí)行。 |
|
<catch event="connection.disconnect.hangup">
<submit namelist="myExit" next="http://mysite/exit.jsp"/>
</catch>
|
|
????然而,在后期處理狀態(tài),應用必須保持跳轉狀態(tài),且不可以進入等待狀態(tài)(詳見4.1.8節(jié)),因此,在后期處理狀態(tài),應用不應該進入<field>或<record>或<transfer>。如果此時VoiceXML應用試圖進入等待狀態(tài),VoiceXML解釋器必須退出。 |
|
????除了上面的限制,VoiceXML應用的執(zhí)行可以在后期處理狀態(tài)正常繼續(xù)。例如,應用在后期處理狀態(tài)可在文檔之間跳轉,如果沒有任何符合條件的form item被選定的話(詳見2.1.1節(jié)),解釋器必須退出。 |