| CallXML 3.0 Development Guide | Home | Frameset Home |
|
<?xml version="1.0" encoding="UTF-8" ?>
<contacts>
<contact type="person||group" sex="male||female" name="Firstname Lastname" alterego="Public Persona" dept="Department Name">
<address type="phone" name="home||cell||office"> 1112223333 </address>
</contact>
</contacts>
</xml>
<address> node, just so we can show off, and illustrate how we can pick out node values, and attribute values. The base template that we will use is printed below, which will look pretty basic to most of us who have worked with XML in any capacity, and suspiciously familiar to rabid comic book nerds. In our XML document, we define a list of costume-wearing employees for The Watchmen Corporation, each of which has an entry for name, department, and phone number in our data structure. We don't bother accessing our other defined attribute values in this tutorial, as they are really just for show...but those developers eager to hone their skills are encouraged to give it a try to improve their understanding of XPath.
<?xml version="1.0" encoding="UTF-8" ?>
<contacts>
<contact type="person" sex="male" name="Edward Blake" alterego="The Comedian" dept="Government Affairs">
<address type="phone" name="cell"> 4079990000 </address>
<address type="phone" name="home"> 4072223333 </address>
</contact>
<contact type="person" sex="male" name="Jon Osterman" alterego="Doctor Manhattan" dept="Special Talents">
<address type="phone" name="cell"> 4071112222 </address>
</contact>
<contact type="person" sex="male" name="Walter Kovacs" alterego="Rorschach" dept="Vigilantism">
<address type="phone" name="office"> 4074445555 </address>
<address type="phone" name="cell"> 4075556666 </address>
<address type="phone" name="home"> 4076667777 </address>
</contact>
<contact type="person" sex="male" name="Dan Dreiberg" alterego="Nite Owl" dept="Retirement">
<address type="phone" name="cell"> 4078889999 </address>
</contact>
<contact type="person" sex="male" name="Adrian Veidt" alterego="Ozmandias" dept="Global Domination">
<address type="phone" name="cell"> 4074446666 </address>
</contact>
<contact type="person" sex="female" name="Laurie Juspeczyk" alterego="Silk Spectre" dept="Unemployment">
<address type="phone" name="cell"> 4078884444 </address>
</contact>
</contacts>
<xml> and <callxml> declarations, but we are also going to add the <fetch> of the XML document right at the very start. This sensibly named element is what we invoke to grab data from an external source, (our "contacts.xml" file), so we can then store the information within the 'var' attribute, and then use XPath expressions to manipulate it based on caller interaction with the application. <fetch>, the entire contents of the XML document are stored in the 'var' attribute, so this is where the XPath comes into play. We are going to use the list function to pull everything from the 'name' and 'dept' attributes of the 'contact' elements and assign it to callxml variables. Then, the fun begins:
<?xml version="1.0" encoding="UTF-8" ?>
<callxml version="3.0">
<fetch var="myContacts" value="contacts.xml" type="xml"/>
<assign var="contactChoices" expr="list(/var/myContacts/contact/@name)"/>
<assign var="deptChoices" expr="list(/var/myContacts/contact/@dept)"/>
<log>*** CONTACT CHOICES: $contactChoices; ***</log>
<log>*** DEPT CHOICES: $deptChoices; ***</log>
</callxml>
<fetch> variable, ($contacts;), within our assignation's 'value' attribute, so as to map the correct XML document to grab the data from. In real-world implementations, a document would likely have numerous <fetch> statements<log> statement early in the game, just so we can see exactly what we are working with as we watch the debugger logs:
*** CONTACT CHOICES: Jon Osterman, Walter Kovacs, Dan Dreiberg, Adrain Veidt, Laurie Juspeczyk ***
*** DEPT CHOICES: Special Talents, Vigilantism, Retirement, Global Domination, Unemployment ***
<prompt> element as our primary method of outputting audio. The <prompt> element is a very much improved version of the <playaudio> tag:<prompt value="alpha, bravo, charlie"><prompt>, we will import our ordered listing of values as a voice grammar by referencing the variable names that we saved this list to within the 'choices' attribute of <prompt>. We also add an option for 'directory' in the event that our callers want to hear a listing of employees. In such an eventuality, we simply have an <on> event handler that loops through the listing via the 'foreach' attribute, and renders it to the caller. Note how the <prompt> nested within this handler inherits the value of the 'foreach' attribute defined in the parent element, which illustrates a fundamental feature of CallXML 3.0: inheritance of container element values. As <prompt> is not defined with any value at all, it will inherently take on the value of the 'foreach' attribute in the containing<on> element.
<?xml version="1.0" encoding="UTF-8" ?>
<callxml version="3.0">
<fetch var="myContacts" value="contacts.xml" type="xml"/>
<assign var="contactChoices" expr="list(/var/myContacts/contact/@name)"/>
<assign var="deptChoices" expr="list(/var/myContacts/contact/@dept)"/>
<log>*** CONTACT CHOICES: $contactChoices; ***</log>
<log>*** DEPT CHOICES: $deptChoices; ***</log>
<prompt value="hello and welcome to the Watchmen Corporation"/>
<prompt label="main" repeat="4" maxtime="8s"
value="say the name of the person or department you want to call" choices="$contactChoices;, $deptChoices;, directory"/>
<on event="maxtime" next="#main">
<prompt value="sorry i didnt hear anything"/>
</on>
<on event="choice:nomatch" next="#main">
<prompt value="im sorry, i didn't get that"/>
</on>
<on event="choice:directory" foreach="you can say any of the following:, $contactChoices;" next="#main">
<prompt/>
</on>
<on event="choice" value="$contacts;" next="#contactType">
<assign var="entity" value="$session.lastchoice;"/>
</on>
</callxml>
<on> elements to shunt our callers to different locations in the application flow based on their choice when this menu plays. When a caller actually does choose a valid contact name, or department name, that's where things get a bit more interesting, as we shall soon see.<do label="contactType"> as our "parent" container, and perform some conditional logic based on the user's contact match to our voice grammar. All this fancy logic is in place to disambiguate whether or not the match was a person's name, or a department name. What we are going to do in this case, is to compare the utterance with the the values defined in our $contactChoices; and $deptChoices; variables to see which list value matches the utterance. How the heck do we do that? We have a couple of steps here, but lets take them one by one.
Jon Osterman, Walter Kovacs, Dan Dreiberg, Adrian Veidt, Laurie Juspeczyk, Edward Blake
<do foreach="$contactChoices;" var="personOrDept">
<if value="$session.lastchoice;" value-is="$personOrDept;">
..
<if> statement:<if value="Walter Kovacs" value-is="Walter Kovacs"><if> statement will evaluate to "false", and we skip down to the next set of <do> and <if> containers, where similar logic applies. As our container element in this case specifies the value of the "department" nodes that we collected:
Special Talents, Vigilantism, Retirement, Global Domination, Unemployment, Government Affairs
<do foreach="$deptChoices;" var="personOrDept">
<if value="$session.lastchoice;" value-is="$personOrDept;">
..
<assign var="addressCount" expr="count(/var/myContacts/contact[@name='$entity;']/address)"/>
..
<assign var="addressCount" expr="count(/var/myContacts/contact[@dept='$entity;']/address)"/>
<address> nodes a given contact match has. Note that this is different for each type of match, (person, or department), we have, and that our Xpath statement specifies a different condition to evaluate against. If there is only a single match, we can go ahead and place the call after we assign the single contact number value to the "number" variable:
<assign var="number" expr="/var/myContacts/contact[@name='$entity;']/address/text()" test="$addressCount; = 1"/>
<assign var="number" expr="/var/myContacts/contact[@dept='$entity;']/address/text()" test="$addressCount; = 1"/>
<goto> elemenet, which will still need to have the same conditional logic by way of the 'test' attribute. If we omitted this attribute, then the call flow would skip down to the 'call' container immediately, even if we had more than one addressCount, which would result in our 'number' variable, (ie the phone number to dial), never being defined....that would be Bad.
<goto value="#call" test="$addressCount; = 1"/>
<assign var="addressChoices" expr="list(/var/myContacts/contact[@name='$entity;']/address/@name )" test="$addressCount; != 1"/>
..
<assign var="addressChoices" expr="list(/var/myContacts/contact[@dept='$entity;']/address/@name )" test="$addressCount; != 1"/>
<do label="contactType">
<do foreach="$contactChoices;" var="personOrDept">
<if value="$session.lastchoice;" value-is="$personOrDept;">
<assign var="entityType" value="'contact'"/>
<assign var="addressCount" expr="count(/var/myContacts/contact[@name='$entity;']/address)"/>
<!-- address count = 1 -->
<assign var="number" expr="/var/myContacts/contact[@name='$entity;']/address/text()" test="$addressCount; = 1"/>
<goto value="#call" test="$addressCount; = 1"/>
<!-- address count is greater than 1 -->
<assign var="addressChoices"
expr="list(/var/myContacts/contact[@name='$entity;']/address/@name )" test="$addressCount; != 1"/>
</if>
</do>
<do foreach="$deptChoices;" var="personOrDept">
<if value="$session.lastchoice;" value-is="$personOrDept;">
<assign var="entityType" value="'dept'"/>
<assign var="addressCount" expr="count(/var/myContacts/contact[@dept='$entity;']/address)"/>
<!-- address count = 1 -->
<assign var="number" expr="/var/myContacts/contact[@dept='$entity;']/address/text()" test="$addressCount; = 1"/>
<goto value="#call" test="$addressCount; = 1"/>
<!-- address count is greater than 1 -->
<assign var="addressChoices"
expr="list(/var/myContacts/contact[@dept='$entity;']/address/@name )" test="$addressCount; != 1"/>
</if>
</do>
</do>
<contact type="person" sex="male" name="Walter Kovacs" alterego="Rorschach" dept="Vigilantism" id="3">
<address type="phone" name="office"> 4074445555 </address>
<address type="phone" name="cell"> 3212021726 </address>
<address type="phone" name="home"> 4076667777 </address>
</contact>
<assign var="addressChoices" expr="list(/var/myContacts/contact[@name='$entity;']/address/@name )" test="$addressCount; != 1"/>
office, home, cell
<prompt value> attribute. We duplicate these definitions within the "choice" attribute, so that our callers can actually input the values when prompted:
<prompt label="addressMenu" repeat="3" value="$addressChoices;, or, cancel" choices="$addressChoices;, cancel" maxtime="8s" next="#main" test="$addressCount; != 1">
..
</prompt>
<prompt label="addressMenu"
repeat="3"
value="$addressChoices;, or, cancel"
choices="$addressChoices;, cancel"
maxtime="8s"
next="#main" test="$addressCount; != 1">
<on event="maxtime">
<prompt value="sorry i didnt hear anything"/>
</on>
<on event="choice:nomatch">
<prompt value="im sorry, i didn't get that"/>
</on>
<on event="choice:cancel" next="#main"/>
<on event="choice" next="#call">
<prompt value="i heard you say, $session.lastchoice;. i will now connect you with, $entity;"/>
<!-- dept -->
<assign var="number"
expr="/var/myContacts/contact[@dept='$entity;']/address[@name='$session.lastchoice;']/text()"
value="$contacts;" test="$entityType; = 'dept'"/>
<!-- person -->
<assign var="number"
expr="/var/myContacts/contact[@name='$entity;']/address[@name='$session.lastchoice;']/text()"
value="$contacts;" test="$entityType; = 'contact'"/>
</on>
</prompt>
<address> node that matches the 'home, office, cell' listing defined in our grammar choices above. All that remains now, is to place our call...<transfer> tag. The 'value' attribute of this tag defines what number we are going to dial, and we can populate this with our "number" variable that we just defined in Step 5:
<do label="call" next="#main">
<transfer type="bridged" value="$number;"/>
</do>
<?xml version="1.0" encoding="UTF-8" ?>
<callxml version="3.0">
<fetch var="myContacts" value="contacts.xml" type="xml"/>
<assign var="contactChoices" expr="list(/var/myContacts/contact/@name)"/>
<assign var="deptChoices" expr="list(/var/myContacts/contact/@dept)"/>
<prompt value="hello and welcome to the Watchmen Corporation"/>
<prompt label="main" repeat="4" maxtime="8s"
value="say the name of the person or department you want to call" choices="$contactChoices;, $deptChoices;, directory"/>
<on event="maxtime" next="#main">
<prompt value="sorry i didnt hear anything"/>
</on>
<on event="choice:nomatch" next="#main">
<prompt value="im sorry, i didn't get that"/>
</on>
<on event="choice:directory" foreach="you can say any of the following:, $contactChoices;" next="#main">
<prompt/>
</on>
<on event="choice" value="$contacts;" next="#contactType">
<assign var="entity" value="$session.lastchoice;"/>
</on>
<do label="contactType">
<do foreach="$contactChoices;" var="personOrDept">
<log>*** contact choices = $contactChoices;</log>
<log>*** personorDept = $personOrDept;</log>
<if value="$session.lastchoice;" value-is="$personOrDept;">
<assign var="entityType" value="'contact'"/>
<assign var="addressCount" expr="count(/var/myContacts/contact[@name='$entity;']/address)"/>
<!-- address count = 1 -->
<assign var="number" expr="/var/myContacts/contact[@name='$entity;']/address/text()" test="$addressCount; = 1"/>
<!-- address count NEQ 1 -->
<assign var="addressChoices"
expr="list(/var/myContacts/contact[@name='$entity;']/address/@name )" test="$addressCount; != 1"/>
</if>
</do>
<do foreach="$deptChoices;" var="personOrDept">
<log>*** dept choices = $deptChoices;</log>
<log>*** personorDept = $personOrDept;</log>
<if value="$session.lastchoice;" value-is="$personOrDept;">
<assign var="entityType" value="'dept'"/>
<assign var="addressCount" expr="count(/var/myContacts/contact[@dept='$entity;']/address)"/>
<!-- address count = 1 -->
<assign var="number" expr="/var/myContacts/contact[@dept='$entity;']/address/text()" test="$addressCount; = 1"/>
<!-- address count NEQ 1 -->
<assign var="addressChoices"
expr="list(/var/myContacts/contact[@dept='$entity;']/address/@name )" test="$addressCount; != 1"/>
</if>
</do>
<prompt label="addressMenu"
repeat="3"
value="$addressChoices;, or, cancel"
choices="$addressChoices;, cancel"
maxtime="8s"
next="#main" test="$addressCount; != 1">
<on event="maxtime">
<prompt value="sorry i didnt hear anything"/>
</on>
<on event="choice:nomatch">
<prompt value="im sorry, i didn't get that"/>
</on>
<on event="choice:cancel" next="#main"/>
<on event="choice" next="#call">
<prompt value="i heard you say, $session.lastchoice;. i will now connect you with, $entity;"/>
<!-- dept -->
<assign var="number"
expr="/var/myContacts/contact[@dept='$entity;']/address[@name='$session.lastchoice;']/text()"
value="$contacts;" test="$entityType; = 'dept'"/>
<!-- person -->
<assign var="number"
expr="/var/myContacts/contact[@name='$entity;']/address[@name='$session.lastchoice;']/text()"
value="$contacts;" test="$entityType; = 'contact'"/>
</on>
</prompt>
</do>
<do label="call" next="#main">
<transfer type="bridged" value="$number;"/>
</do>
</callxml>
| ANNOTATIONS: EXISTING POSTS |
| login |
|