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>
Leave a comment