Introducing E4X

Exploring E4X

ECMAscript for XML (more colloquially known as E4X) has had comparatively limited success of adoption in the face of the power of JSON, I suspect partially because of a general ambivalence that the JavaScript community has towards working with XML, but more so because there is comparatively little out there describing how best to take advantage of the features that E4X offers.

E4X is an extension to JavaScript that is available to both Firefox and ActionScript developers that turns XML into a native data type on par with strings, arrays, objects, and regular expressions. It is not a DOM representation of XML, but rather is its own distinct entity, something that has its own set of associated methods and operators and that is intended to making it easier to work with XML than using the sometimes cumbersome methods associated with DOM (perhaps the closest analogy I can think of would be the comparison between modeling clay with your bare hands and modeling clay with a waldo while wearing a space suit).

There is a basic question that needs to be answered when addressing new technology. Why work with E4X when you can do the same thing with JSON objects (keeping in mind that both E4X and JSON objects are “part” of JavaScript, though JSON is an expression of the JavaScript Object object)? Objects have a few obvious advantages here, though the advantages are not as great as they may initially appear.

In theory, JSON objects can work anywhere that JavaScript is supported (and according to its proponents, within most server implementations), whereas E4X is only supported in Firefox v. 1.5 and above and in Adobe’s ActionScript 3 (Flash, Adobe AIR, and Adobe FLEX). In practice, the server implementation most likely to end up using JSON is Ruby, whereas PHP, JSP, and .NET in general require that you roll your own parsers and serializers. Moreover, Firefox in general has served as the browser to beat (at least outside of the IE space), and with two providers of E4X services, I suspect that newer versions of Oxgyen and possibly Safari will likely incorporate E4X capabilities (though they will also likely include native JSON capabilities).

JSON is marginally more compact to encode—from perhaps two percent more efficient for attribute intensive XML to perhaps as much as eight percent more efficient for element heavy encodings. This advantage disappears if either resource is zipped, as in both cases what you’re doing is storing label tokens and associated values with the syntactical variations between XML and JSON, accounting for a very small difference (likely less than 0.1 %). Thus, if efficiency of transmission is a concern, gzipping content will occur as a matter of course and the speed becomes moot. Moreover, there is some very interesting work being done right now with Efficient XML Interchange (EXI) at the level of the W3C that will likely make EXI considerably more compact than even compressed JSON.

Creating E4X Objects

What about parsing and serializing? JSON has an obvious advantage over DOM in that regard—DOM parsing is extraordinarily inefficient, the structures associated with DOM are large, and the time it takes to parse an object of any complexity can be an order of magnitude higher than JSON. With E4X, there’s considerably less of an advantage—an E4X serialization takes perhaps 20 % more time than the equivalent JSON implementation—if the insecure eval() statement is used to parse the JSON object. To do JSON securely, you have to use a safe implementation method, which is considerably slower than the equivalent E4X parser, while the E4X parser does not (cannot in fact) automatically expose potentially dangerous imperative code.

Parsing in E4X is considerably simpler than it is from DOM. In the simplest case, the parsing is handled behind the scenes:

var phoneBook = <phoneBook>    
    <phoneEntry>         
        <name>Joe Schwartz</name>         
        <phoneNumber>342-2351</phoneNumber>    
    </phoneEntry>    
</phoneBook>

In this particular case, the XML is entered “as is” directly into the JavaScript code, and is then consequently interpreted as an E4X object by the system. This mechanism is similar to the implicit way that strings, arrays, and objects are created using their own internal notation:

var str = "This is a string";
var arr = [item1,item2,item3];
var obj = {a:"item 1",b:"item 2",c:"item 3"}

As with arrays and objects, an E4X object can extend for more than one line; its “terminator” is the closing bracket of the root node. Of course, because XML can preserve white space, the use of line feeds and the like should respect the white space argument of the XML itself.

Similarly, an E4X object can be created using the new XML() constructor, just as you would expect of arrays and objects. The constructor can take either a string or an E4X object as an argument; the first will be parsed as XML (if it’s well formed), the second will be passed through unchanged.

var phoneBookStr = "<phoneBook>    
    <phoneEntry>         
    <name>Joe Schwartz</name>         
    <phoneNumber>342-2351</phoneNumber>    
    </phoneEntry>    
    </phoneBook>";
var phoneBook = new XML(phoneBookStr);

It should be noted here that, unlike the use of eval() for JSON evaluation, the new XML() constructor is code safe; it won’t evaluate JavaScript functions that may be embedded within the string, even if they are bracketed. You can evaluate JavaScript in implicitly declared E4X by using brackets {} within the code.

var a = "bar";    
var data = <foo id={a+(3+4)}>{a+" "+"bat"}</foo>;
print(data);
=> <foo id="bar7">bar bat</foo>

In the case of attributes, the bracketed expression should not be quoted, as this suppresses the evaluation within the brackets. This approach is useful for creating quick and dirty XML objects with data coming from the containing JavaScript.

If parsing is simple with E4X, serialization is even simpler. You can convert your E4X object into a string by using the toXMLString() method on an E4X object:

var phoneBook = <phoneBook>    
    <phoneEntry>         
       <name>Joe Schwartz</name>         
       <phoneNumber>342-2351</phoneNumber>    
    </phoneEntry>    
</phoneBook>;
var phoneBookStr = phoneBook.toXMLString();
=> "<phoneBook>    
   <phoneEntry>         
      <name>Joe Schwartz</name>         
      <phoneNumber>342-2351</phoneNumber>    
   </phoneEntry>    
</phoneBook>"

The toString() method works a litle differently for E4X objects than it does for other JavaScript objects. Instead of returning the XML representation, it returns the concatenated text of all of the text nodes in the E4X object. This method is implicitly called if the context of the JavaScript up to the point of evaluating the E4X object is a string. This can be made more obvious in the following:

var data=<data>5</data>;
print(data);
//=> 5
var dataStr = "test"+data;
print(dataStr);
//=>"test5"
var dataValue = 10 + parseInt(data);
print(dataValue);
//=> 15
var dataWrap = <wrap>{data}</wrap>
print(dataWrap);
//=><wrap><data>5</data></wrap>

Note that while the string and number representations acted as if the content was a string, when the data variable holding the E4X object was evaluated, it was evaluated as an XML block. Remember also that functions like print automatically call the toString() method on their contents before outputing the results. However, E4X nodes that have subordinate element children are rendered as XML with toString().

The E4X extension recognizes two core XML types: XML() and XMLList(). An XML List is a linear sequence of XML nodes. A given E4X object that has more than a single node will actually be made up of a combination of XML() and XMLList() objects, and in the proper context, an XML() object can also expose the XMLList interface. For instance, consider an expanded phone book:

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>

In this particular case, the <phoneBook> node is an XML() object. However, phoneBook.phoneEntry (which uses the object-like “dot” notation) is an XMLList() object that has a number of specific entries. You can get the actual count with the length() function:

print(phoneBook.phoneEntry.length());
=>4

You can also reference each element of an XML List individually using zero-based array notation:

print(phoneBook.phoneEntry[0]);
=>    
<phoneEntry>         
   <name>Joe Schwartz</name>         
   <phoneNumber>342-2351</phoneNumber>    
</phoneEntry>

This similarity between E4X objects, arrays, and traditional Object objects can also be seen in the use of hashed names; you can reference both XML() and XMLList() objects using [“name“] where name is the name of the element being requested. Thus, the previous statement could also be written as:

var entry0 = phoneBook["phoneEntry"][0];
print(entry0.name);
=> "Joe Schwartz"
print(entry0["name"]);
=> "Joe Schwartz"

This can really come in handy when you’re working with HTML in E4X, because the terms involved in E4X (especially such terms as the “class” attribute) also have meanings in JavaScript:

var p = <p class="myClass">Foo</p>;
print(p.@class);
==> This generates an error, because class is a reserved JavaScript keyword
print(p.@["class"]);
=> "myClass"

Assigning to E4X Objects

One of the coolest aspects of E4X is the power exposed by assignments. Indeed, you can use assignment to quickly build XML structures from scratch, change values on the fly and add and remove new content quickly.

Consider, for instance, the nascent phone book discussed here. The following shows how you can create a new phone book with a single entry:

var phoneBook = <phoneBook/>;
phoneBook.phoneEntry = new XMLList();
var phoneEntry = <phoneEntry/>;
phoneEntry.name = "Jeane Tomasic";
phoneEntry.phoneNumber = "123-4567";
phoneBook.phoneEntry += phoneEntry;
print(phoneBook);
=><phoneBook>
   <phoneEntry>
        <name>Jeane Tomasic</name>
        <phoneNumber>123-4567</phoneNumber>
   </phoneEntry>
<phoneBook>

The second statement—phoneBook.phoneEntry = new XMLList();—indicates that any time phoneBook.phoneEntry is used as an L-Value (is on the left side of an assignment statement) it should be treated as a list (for reasons to be covered momentarily). The next three lines create an individual entry. When an XML() object has a new name (such as .name or .phoneNumber) appended to it, that name will be treated as a new element with that name, holding the value calculated from the right side of the equation. This is similar to the way that objects (as hashes) work, but in the case of objects, what are created are hash keys and values rather than elements and text children as is the case for E4X.

The statement phoneBook.phoneEntry += phoneEntry is, frankly, just cool. It adds the new phoneEntry object thus created to the phoneEntry list. Note that the element name of the added object MUST correspond to the name of the list or this throws an exception. However, in some cases, you may have multiple types of items being added to a given element. In this case you can use a wild card character as the list name, with no need to declare the entity.

var html = <html/>;
html.head.title = "A new XML Document";
html.head.style = <style type="text/css"><![CDATA[
h1 {font-weight:bold;}
     ]]></style>;
html.body.* = new XMLList();
html.body.h1 = "A new XML Document";
html.body.* += <p>This is the first paragraph.</p>;
html.body.* += <p>This is the second paragraph.</p>;
var ul = <ul/>;
ul.* = new XMLList();
ul.* += <li>This is item 1</li>;
ul.* += <li>This is item 2</li>;
html.body.* += ul;
print(html);
=>
<html>
    <head>
        <title>A new XML Document</title>
        <style type="text/css">
            h1 {
                font-weight:bold;
            }</style>
    </head>
    <body>
        <h1>A new XML Document</h1>
        <p>This is the first paragraph.</p>
        <p>This is the second paragraph.</p>
        <ul>
            <li>This is item 1</li>
            <li>This is item 2</li>
        </ul>
    </body>
</html>

This sequence makes a quick web page, doing a few rather subtle tricks to do so. The first is the implicit creation of nodes; the statement html.head.title = "A new XML Document" first checks to see if a <head> element is defined. Since one isn’t, the E4X core automatically creates it before making the <title> child element to it. In the next statement, a CDATA block is used to add content that will not be parsed (avoiding the parsing of the CSS rule contained within the <style> element).

The statement html.body.* = new XMLList(); tells the E4X object that if new items are added to the * object (a wild card) these should be assumed to be added as a child to the list contained by the <body> element. This can be overridden explicitly (the <h1> statement does just this), but normally when objects are added via the += operator, they will then be added to the list in the order assigned. Thus, html.body.* += <p>This is the first paragraph.</p> adds a new paragraph to the body, the next statement adds a second paragraph to the body and so forth. The <ul> statement represents this same principle in miniature, and shows how you can add pieces to a larger structure without having to carry around the reference node.

The benefit of this approach is somewhat limited if you’re just building an XML or XHTML structure – it’s probably more efficient to just use a regular XML editor. However, this approach can come into its own when you’re trying to combine XML and JavaScript. For instance, suppose that you had an object version of the phone book and you wanted to create a table showing the names and numbers of each person in the book. You can combine the above approach with JSON iteration to quickly create the table:

var _phoneBook= {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"}
     ]};
var table = <table/>;
table.tr = new XMLList();
table.tr.th= new XMLList();
table.tr.th += <th>Name</th>;
table.tr.th += <th>Phone Number</th>;
for each (var entry in _phoneBook.phoneEntry){
     table.tr += <tr>
         <td>{entry.name}</td>
         <td>{entry.phoneNumber}</td>
       </tr>
     }
print(table);
=>
<table>
    <tr>
        <th>Name</th>
        <th>Phone Number</th>
    </tr>
    <tr>
        <td>Joe Schwartz</td>
        <td>342-2351</td>
    </tr>
    <tr>
        <td>Aleria Delamare</td>
        <td>342-7721</td>
    </tr>
    <tr>
        <td>Susan Sto Helit</td>
        <td>315-2987</td>
    </tr>
    <tr>
        <td>Kyle Martin</td>
        <td>342-7219</td>
    </tr>
</table>

Once created, the table can then be added into an existing element via the innerHTML property.

It’s also possible to remove an item from an existing E4X object via the delete command. For instance, to remove the third item (Susan Sto Helit) from the table you just created, you simply use the command:

delete table.tr[3]

though a warning here is in order, unlike other XML technologies such as XPath, E4X is zero based, so that table.tr[3] is actually the fourth row in the table, but the first row is the header, bumping the index up one. If you wanted to exclude the header row, you’d use table.tr.(td)[3], as discussed below.

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.

In this particular example, when the document initially loads, it asynchronously retrieves the atom feed from the server via the populate() function using an XMLHttpRequest call. The goal here is to minimize the amount of processing of the feed as a DOM, so here that process is handled by using the http.responseText method to get the feed as text. The Mozilla E4X implementation has a few serious limitations, one of the biggest of which is its inability to parse XML declarations that start with <? and end with ?>. Thus, it is necessary to run a quick regular expression to remove all of these:

var data = data.replace(/<\?(.*?)\?>/g,"")

Namespaces in E4X are something of a headache, for much the same reason that they are a headache in XML in general. However, it is possible to change the default namespace and work within the context of that namespace without needing to create a formal namespace declaration or use prefixes. In order to retrieve the feeds from the relevant returned document (which includes various HTTP request and response headers as part of the package), the default namespace was set to Atom as “http://www.w3.org/2005/Atom”:

    default xml namespace="http://www.w3.org/2005/Atom";
    var feed = new XML(data);

Once so declared, it becomes possible to switch back and forth between then Atom namespace and the XHTML namespace, jumping from reading data appropriate to each entry to making new option elements for that entry:

    for each (entry in feed..entry){
    default xml namespace="http://www.w3.org/2005/Atom";
    var title = entry.title.toString();          
    var id = entry.id;
    default xml namespace="";
    var option = <option value={id}>Introducing E4X</option>.toXMLString();
    selectNode.innerHTML += option;
    }

Note the use of the toString() and toXMLString() methods. E4X exposes a limited set of methods on E4X XML() and XMLList() objects (you don’t have the same ability to assign methods to E4X objects that you do with ordinary objects for this reason). A reference such as entry.title actually returns an XML node; if this is used as part of a string expression then the JavaScript engine will automatically recast the node to its text value, but sometimes it is better to make this explicit via the toString() method (you could also do “”+entry.title to do the casting, but that operation isn’t always obvious when looking at code). Similarly, the toXMLString() method will serialize an XML() node as XML content, while applying toXMLString() to an XMLList() object will return a serialized collection of nodes.

The mapping to the default namespace prior to the option will set the namespace to the namespace of the containing object, which in this case is the <select> node, if passed to the innerHTML property.

One of the more useful aspects of JavaScript as a language is the use of closures, in which variables that are defined at one point, then used within a function defined in the context of those variables, will continue to hold the values previously assigned so long as the function remains in scope.

This is used to good effect in the internally defined selectFeed() function. The feed is referenced within the function, as well as displayNode and linkNode, which defines the display area for a selected entry’s content and a button for launching the original page in a second window. One interesting consequence of this is that because the selectFeed() function is then assigned at that point to the selection box listing the titles, the feed itself remains in memory, but is completely inaccessible to anything beyond the selectFeed() function. Admittedly, in this case it doesn’t make that much difference (determining the feed is relatively simple), but from a security standpoint this provides a layer of protection on potentially sensitive information.

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);

I wanted to include the link to the source not only because it’s a logical piece of a feed viewer, but because it’s useful to illustrate an attribute filter in E4X. In Atom, a given entry may have more than one link associated with that entry. The rel=”alternate” link is typically the one that contains a reference to the relevant web page that the resource came from initially (and as such there is always only one such alternate link). The expression entry.link.(@rel=='alternate').@href thus retrieves from the link collection the link for which the @rel (relationship) tag is set to “alternate”. The expression within the parentheses here is (more or less) JavaScript; the @rel retrieves the rel attribute relative to the link collection and compares it to the string ‘alternate’ (note the double equal signs). This is a filter, just as var entry = feed..entry.(id==selectNode.value); in the previous line is a filter.

A screenshot of the “app” is shown in Figure 1.


Figure 1. Metaweb Screenshot

At the end of this method you have the expression linkNode.setAttribute("href",link). This is a DOM statement, because linkNode is a DOM element, not an E4X element. At this stage, while one of the main goals of the E4X process was to create a simpler, more efficient way of working with document objects, this has not been realized in any significant way.

By the way, it is possible to convert an E4X object into a DOM tree and vice versa, though these are comparatively expensive operations. The function eNode2dNode performs the conversion one way, converting an E4X object into the root node of its own DOM-based document, while dNode2eNode makes the conversion in the other direction:

var eNode2dNode = function(eNode){ 
    var str = eNode.toXMLString(); 
    var doc = (new DOMParser()).parseFromString(str,"text/xml"); 
    return doc.documentElement.cloneNode(true); 
    }
    
var dNode2eNode = function(dNode){
    var str = (new XMLSerializer()).serializeToString(dNode);
    var eNode = new XML(str);
    return eNode;
    }

E4X and JSON

It is not my wish to deprecate the importance or value of JSON in this article (well, not much anyway). JSON represents a minimally sufficient platform for information interchange within most browsers, and as such realistically will be around for quite some time (especially as Microsoft’s Internet Explorer does not look like it will be adopting the most recent JavaScript changes any time soon).

However, it’s worth noting that a lightweight XML protocol, LINQ, likely will be migrating to IE with its next release. While differing somewhat in syntax from E4X (and having a considerably broader role), LINQ will most likely be doing much the same duty in IE that E4X does in Firefox and Flash—providing a way of using XML easily and cleanly without having to use the rather cumbersome mechanism of DOM. Given the increasing dominance of XML as a messaging and transport protocol on the server and between server and client, the use of LINQ does open up the notion that you can take advantage of the rich characteristics that XML has to offer without having to complexify your code with DOM manipulation.

At a minimum, if you are specifically targeting the frameworks where E4X is supported, you should take some time to investigate the technology, especially when dealing with the increasingly syndicated nature of web technologies. Combining E4X and Atom, for instance, opens up all kinds of interesting potential usages, especially given the increasing role that Atom is playing as a data transport protocol for companies such as Google. While it is possible that you’ll see more companies exposing JSON services, I personally see XML-based Atom services growing far faster, and in that case the use of a native XML datatype just cannot be beat.

Leave a Reply

Your email address will not be published. Required fields are marked *