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

advertisement

Introducing E4X

November 30, 2007

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"

Pages: 1, 2, 3, 4

Next Pagearrow