XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Introducing E4X
by Kurt Cagle | Pages: 1, 2, 3, 4

Iterations and Filters

This use of iterating through an object represent another area where E4X equalizes the field. One advantage that JSON has over DOM is the fact that iterations in JSON are very straightforward, while they are cumbersome and awkward in DOM. However, E4X was designed specifically with iterations in mind, and in some respects is considerably more efficient than JSON in that regard. For instance, suppose that you wanted to look through a set of phone numbers to find the ones that are in the "342" local exchange (the first three numbers of the seven digit version of the telephone number). Both JSON and E4X would render this as

for each (phoneNumber in phoneNumbers.phoneNumber){
    if (phoneNumber.indexOf("342")==0){print(phoneNumber);}
    }

However, suppose that you had a more complex structure, of the form:

phonebook
   phone-entry
       name
       phone-number

In XML this might be an instance of the form:

var phoneBook = <phoneBook>    
    <phoneEntry>         
        <name>Joe Schwartz</name>         
        <phoneNumber>342-2351</phoneNumber>    
    </phoneEntry>    
    <phoneEntry>         
        <name>Aleria Delamare</name>         
        <phoneNumber>342-7721</phoneNumber>    
    </phoneEntry>    
    <phoneEntry>         
        <name>Susan Sto Helit</name>         
        <phoneNumber>315-2987</phoneNumber>    
    </phoneEntry>    
    <phoneEntry>         
        <name>Kyle Martin</name>
         <phoneNumber>342-7219</phoneNumber>    
    </phoneEntry> 
</phoneBook>

while as an object, this same list would be given as:

    {
     phoneEntry:[
        {
          name:"Joe Schwartz", 
          phoneNumber:"342-2351"
        }, 
        {
          name:"Aleria Delamare", 
          phoneNumber:"342-7721"
        }, 
        {
          name:"Susan Sto Helit", 
          phoneNumber:"315-2987"
        }, 
        {
          name:"Kyle Martin", 
          phoneNumber:"342-7219"
        }
     ]}

To retrieve a list of all of the phone entries for a given exchange, the E4X format would be given as:

for each (entry in 
     phoneBook.phoneEntry.(phoneNumber.indexOf("342")==0)){
        print(entry.name+":"+entry.phoneNumber);     
        }

while the equivalent object based entry becomes:

for each(entry in _phoneBook.phoneEntry){     
    if (entry.phoneNumber.indexOf("342")==0){              
        print(entry.name+":"+entry.phoneNumber);              
        }      
    }

This subtle difference can add up when you're dealing with dozens or hundreds of entries, as the E4X is performed as a binary filtering, whereas the pure object equivalent is handled as a script call. The more complex the conditional filter(s), the more the advantage shows up in favor of E4X. Additionally, because E4X is a set-based language, the above can be rendered as:

var entries = phoneBook.phoneEntry.(phoneNumber.indexOf("342")==0);
for each (entry in entries){
    print (entry.name+":"+entry.phoneNumber);
    }

To do this in object-based JavaScript requires that you create a list and populate it:

var _entries = [];
for each (var entry in _phoneBook.phoneEntry){
    if (entry.phoneNumber.indexOf("342")==0){
        _entries.push(entry)
        }
    }
for each (entry in _entries){
    print (entry.name+":"+entry.phoneNumber);
    }

giving you four lines vs. nine.

Other than the rather annoying inability for an element to reference itself in a filter (the part contained in an expression such as the following, shown in bold):

phoneBook.phoneEntry.(phoneNumber.indexOf("342")==0)

the E4X code for filtering (using JavaScript expressions relative to a given context) is generally considerably more intuitive and usually terser than the equivalent expressions with JavaScript objects.

Building a Feed Application with E4X

Perhaps one of the biggest benefit of using E4X stems from the fact that most database systems (and consequently many web sites) are now moving towards producing XML content natively, whereas relatively few are doing the same thing with JSON feeds (which consequently need to be crafted by hand). Consider, for instance, the process of retrieving an XML-based Atom feed from my blog (at http://www.metaphoricalweb.org/?q=atom/feed) to retrieve the titles of each entry in the feed, and place them as options in a select box, as shown in Listing 1.

Listing 1. Showing Metaphorical Web Atom Feeds
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <script type="application/x-javascript"><![CDATA[
var populate=function(selectId,displayId,linkId,feed){
  var http = new XMLHttpRequest();
  var passFeed = "feed.xq?path="+feed;
  http.open("get",passFeed,true);
  http.onload = function(){
    var selectNode = document.getElementById(selectId);
    selectNode.innerHTML = "";
    var displayNode = document.getElementById(displayId)
    var linkNode = document.getElementById(linkId);
    var data = http.responseText;
    var data = data.replace(/<\?(.*?)\?>/,"")
    default xml namespace="http://www.w3.org/2005/Atom";
    var feed = new XML(data);
    for each (entry in feed..entry){
         default xml namespace="http://www.w3.org/2005/Atom";
          var title = entry.title.toString();          
          var link = entry.link.(@rel='alternate').@href.toString();
          var id = entry.id;
          default xml namespace="";
          var option = <option value={id}>Introducing E4X</option>.toXMLString();
          selectNode.innerHTML += option;
          }
    var selectFeed = function(){    
        default xml namespace="http://www.w3.org/2005/Atom";
        var entry = feed..entry.(id==selectNode.value);
        var link = entry.link.(@rel='alternate').@href.toString();        
        var content = '<div xmlns="http://www.w3.org/1999/xhtml"><h2>'+
            entry.title+'</h2>'+entry.content.text().toString()+'</div>';
        var content= content.replace(/<\?(.*?)\?>/g,"")
        default xml namespace="http://www.w3.org/1999/xhtml";
        displayNode.innerHTML = content;
        linkNode.setAttribute("href",link);        
        }; 
    selectNode.addEventListener("change",selectFeed,false);
    default xml namespace="http://www.w3.org/2005/Atom";
    selectNode.value =feed..entry[0].id.valueOf();
    selectFeed();    
    }
  http.send(null);
  }            
  ]]></script>
        <style type="text/css"><![CDATA[
#display {width:600px;height:400px;overflow-y:auto;
    border:solid 2px gray;padding:5px;-moz-border-radius:8px;
    background-color:#ffef80;}
h1 {font-size:18pt;font-family:Times New Roman;}
h2 {font-size:14pt;font-family:Times New Roman;font-style:italic;}
            ]]></style>
    </head>
    <body onload="populate('s1','display',
          'http://www.metaphoricalweb.org/?q=atom/feed')">
        <h1>Metaphorical Web</h1>
        Article: <select id="s1"/>
        <a href="" id="link" target="new">Open</a>
        <br/>
        <div id="display">Select an article to review.</div>
    </body>
</html>

This particular function relies upon a server "router" for passing feeds, in this case a short eXist database XQuery called feed.xq called as a web service which takes the URI passed as part of a "path" parameter and retrieves the document associated with it:

    (: feed.xq :)
    declare namespace atom="http://www.w3.org/2005/Atom";
    declare namespace h="http://www.w3.org/1999/xhtml";
    let $path := request:get-parameter("path","")
    let $doc := httpclient:get(xs:anyURI($path),false(),<headers />)
    return $doc

This exercise is also making the implicit assumption that the syndication feed is in Atom format, though the same basic rules apply for most XML-compliant RSS 2.0 feeds.

Pages: 1, 2, 3, 4

Next Pagearrow