Deciphering E4X

| | Comments (0) | TrackBacks (0)
Having been using E4X on the server side and on the command line to manipulate XML documents for some months now, I went back to see if I could learn a couple things from my older code that would result in a few best practices and idioms for using E4X.

After starting to document what I thought were best practices I decided to write a couple tests to make sure what I thought was happening really was happening.

Below is the output of some simple tests that try to unwind the relationship between an XML object and an XMLList object.

First test: an xml element with no children

Consider the xml:

var results = <results/>

And tests:

results.*.length() : 0
results.row.length() : 0
typeof( results.* ) : xml
results.* instanceof XML : false
results.* instanceof XMLList : true
typeof( results.row ) : xml
results.row instanceof XML : false
results.row instanceof XMLList : true
typeof( results.row[ 0 ] ) : undefined
results.row[ 0 ] instanceof XML : false
results.row[ 0 ] instanceof XMLList : false
results.row.item == "A" : false
results.row.item.toString()  : ''
results.row.item instanceof XML : false
results.row.item instanceof XMLList : true
results.row.item[ 0 ] == "A" : false
results.row.item[ 0 ] instanceof XML : false
results.row.item[ 0 ] instanceof XMLList : false
results.row.item.text() == "A" : false
results.row.item.text() instanceof XML : false
results.row.item.text() instanceof XMLList : true
results.row.item[ 0 ].text() == \"A\" : TypeError: Cannot call method "text" of undefined

Note, results.* (synonymous with .children()), results.row, and results.row.item.text() always returns an XMLList, even if it's empty. Additionally a reference to an element in the list does not fail with an error, it simply returns an undefined object. Thus, results.row.item[ 0 ] == "A" becomes undefined == "A", which is false of course. But calling results.row.item[ 0 ].text() does throw an error and calling results.row.item.text()[ 0 ] is dangerous if there are any blank text nodes or none at all. For example:

<value>{ results.item.text()[ 0 ] }</value>.toXMLString() : <value>undefined</value>

Second test: a single child element

Now consider:

var results = 
<results>
  <row>    
    <item>A</item>  
  </row>
</results>

With tests:

results.*.length() : 1
results.row.length() : 1
typeof( results.* ) : xml
results.* instanceof XML : false
results.* instanceof XMLList : true
typeof( results.row ) : xml
results.row instanceof XML : false
results.row instanceof XMLList : true
typeof( results.row[ 0 ] ) : xml
results.row[ 0 ] instanceof XML : true
results.row[ 0 ] instanceof XMLList : false
results.row.item == "A" : true
results.row.item.toString()  : 'A'
results.row.item instanceof XML : false
results.row.item instanceof XMLList : true
results.row.item[ 0 ] == "A" : true
results.row.item[ 0 ] instanceof XML : true
results.row.item[ 0 ] instanceof XMLList : false
results.row.item.text() == "A" : true
results.row.item.text() instanceof XML : false
results.row.item.text() instanceof XMLList : true
results.row.item[ 0 ].text() == "A" : true
results.row.item[ 0 ].text() instanceof XML : false
results.row.item[ 0 ].text() instanceof XMLList : true
typeof( results.row.item.text()[ 0 ] ) : xml
results.row.item.text()[ 0 ] == "A" : true
results.row.item.text()[ 0 ] instanceof XML : true
results.row.item.text()[ 0 ] instanceof XMLList : false

Note that an XMLList is always returned unless a specific element is selected by index, even subsequent references after an indexed reference.For example, results.row.item[ 0 ].text() returns an XMLList if ..item[0] is not undefined.Even though results.row.item is not an XML instance, the statement results.row.item == "A" is true since the XMLList of text nodes will concatenate into a string. For example:

<><item>A</item><item>B</item></>.text().toString() : AB

Third test: multiple children with missing text in second item element

And finally xml:

var results = 
<results>
  <row>
    <item>A</item>
  </row>
  <row>
    <item></item>
  </row>
</results>

And tests:

results.*.length() : 2
results.row.length() : 2
typeof( results.* ) : xml
results.* instanceof XML : false
results.* instanceof XMLList : true
typeof( results.row ) : xml
results.row instanceof XML : false
results.row instanceof XMLList : true
typeof( results.row[ 0 ] ) : xml
results.row[ 0 ] instanceof XML : true
results.row[ 0 ] instanceof XMLList : false
results.row.item == "A" : false
results.row.item.toString()  : '<item>A</item><item/>'
results.row.item instanceof XML : false
results.row.item instanceof XMLList : true
results.row.item[ 0 ] == "A" : true
results.row.item[ 0 ] instanceof XML : true
results.row.item[ 0 ] instanceof XMLList : false
results.row.item.text() == "A" : true
results.row.item.text() instanceof XML : false
results.row.item.text() instanceof XMLList : true
results.row.item[ 0 ].text() == "A" : true
results.row.item[ 0 ].text() instanceof XML : false
results.row.item[ 0 ].text() instanceof XMLList : true
typeof( results.row.item.text()[ 0 ] ) : xml
results.row.item.text()[ 0 ] == "A" : true
results.row.item.text()[ 0 ] instanceof XML : true
results.row.item.text()[ 0 ] instanceof XMLList : false

And here we know results.row.item is an XMLList, but results.row.item == "A" is false since the list has two elements. Yet results.row.item.text() == "A" is true since there is only one text node under the item elements. For example:

<><item>A</item><item>B</item></>.toString() : <item>A</item><item>B</item>

0 TrackBacks

Listed below are links to blogs that reference this entry: Deciphering E4X.

TrackBack URL for this entry: http://www.manamplified.org/cgi-bin/mt-tb.cgi/299

Leave a comment