Javascript
and XML Guide
XML and XMLList Javascript Object.
Constructing XML using Templates
XML and Code-assist (Intellisense)
Accessing an XML Document and its values
Setting XML Document on an XML Resource
See also: Javscript Scripting Menu, Javascript Editor, API Javadoc
E4X is an ECMA (Javascript) standard. It provides an API for creating and processing XML documents using Javascript.
This document provides an introduction to E4X (ECMAScript for XML). ECMA-262 (Javascript 1.3) was standardized in December 1999. ECMA-357 (E4X) was standardized in June 2004. E4X is an extension to Javascript. Further information regarding the E4X specification can be found here. Previous knowledge of XML and Javascript is assumed when reading this document.
The E4X API consists of 4 native XML Javascript objects. The Global object is an extension to the ECMA Script Global object.
See table below for more information on the basic XML Javascript types:
Javascript Object |
Description |
XML |
An XML base object. This can represent a document or element. |
XMLList |
Consists of multiple XML elements of the same name. |
Namespace |
Namespace objects represent XML namespaces and provide an association between a namespace prefix and a Unique Resource Identifier (URI). The prefix is either the undefined value or a string value that may be used to reference the namespace within the lexical representation of an XML value. |
QName |
QName objects are used to represent qualified names of XML elements and attributes. Each QName object has a local name of type string and a namespace URI of type string or null. When the namespace URI is null, this qualified name matches any Namespace. |
Global |
Adds an additional method XMLName(value) to Global. |
To instantiate an object is the same as instantiating any other Javascript object:
//new
xml object
var
x = new XML();
//new
date object
var
d = new Date();
//new
array
var
a = new Array();
All XML objects are an extension of Javascript Object.
E4X also provides many Java DOM-like capabilities. The
methods of the XML object include the following methods. Objects marked with a
* are also available on XMLList objects.
Methods of the XML object |
Meaning |
addNamespace(namespace) |
Adds the namespace to the in-scope namespaces of the
element. |
appendChild(child) |
Adds child as a new child of the element, after all other
children. |
attribute(attributeName) * |
Returns the attribute of with the requested name. |
attributes() * |
Returns the attributes of this element. |
child(propertyName) * |
Returns the child element with the given propertyName, or
if propertyName is an integer, returns the child in that position. |
childIndex() |
Returns the index of this child among its siblings. |
children() * |
Returns all the children of this object. |
comments() * |
Returns all the comments that are children of this XML
object. |
contains(value) * |
Compares this element with the value, primarily provided
to enable an XML element and an XML list to be used interchangeably. |
copy() * |
Returns a deep copy of the element. The parent property
of the copy will be set to null. |
descendants([name]) * |
Returns the descendant elements (children, grandchildren,
etc.). If a name is provided, only elements with that name are returned. |
elements([name]) * |
Returns the child elements. If a name is provided, only
elements with that name are returned. |
hasComplexContent() * |
Returns true for elements with child elements, otherwise
false. |
hasSimpleContent() * |
Returns true for attributes, text nodes, or elements
without child elements, otherwise false. |
inScopeNamespaces() |
Returns an array of Namespace objects representing the
namespaces in scope for this object. |
insertChildAfter(child1, child2) |
Inserts child2 immediately after child1 in the XML
object's children list. |
insertChildBefore(child1, child2) |
Inserts child2 immediately prior to child1 in the XML
object's children list. |
length() * |
Returns 1 for XML objects (allowing an XML object to be
treated like an XML List with a single item.) |
localName() |
Returns the local name of this object. |
name() |
Returns the qualified name of this object. |
namespace([prefix]) |
Returns the namespace associated with this object, or if a
prefix is specified, an in-scope namespace with that prefix. |
namespaceDeclarations() |
An array of Namespace objects representing the namespace
declarations associated with this object. |
nodeKind() |
A string representing the kind of object this is (e.g.
"element"). |
normalize() * |
Merge adjacent text nodes and eliminate empty ones. |
parent() * |
The parent of this object. For an XML List object, this
returns undefined unless all the items of the list have the same parent. |
processingInstructions([name]) * |
A list of all processing instructions that are children
of this element. If a name is provided, only processing instructions matching
this name will be returned. |
prependChild(value) |
Add a new child to an element, prior to all other
children. |
removeNamespace(namespace) |
Removes a namespace from the in-scope namespaces of the
element. |
replace(propertyName, value) |
Replace a child with a new one. |
setChildren(value) |
Replace the children of the object with the value
(typically an XML List). |
setLocalName(name) |
Sets the local name of the XML object to the requested
value. |
setName(name) |
Sets the name of the XML object to the requested value
(possibly qualified). |
setNamespace(ns) |
Sets the namespace of the XML object to the requested
value. |
text() * |
Concatenation of all text node children. |
toString() * |
For elements without element children, returns the values
of the text node children. For elements with element children, returns same
as toXMLString. For other kinds of objects, the value of the object. |
toXMLString() * |
Serializes this XML object as parse-able XML. |
valueOf() * |
Returns this XML object. |
E4X introduces a new type of “XML” object which holds an XML element. These objects are created by writing XML directly or by creating a new XML object:
//write XML directly using Literal XML
var customer = <customer id="A1101">
<firstName>Fred</firstName>
<lastNameName>Fred</lastName>
</customer>
//write XML using new XML()
var order = new XML(<order id="1111"><part
id="12345" quantity="2"/></order>);
The XML Object can be constructed with:
//Example:
var
xml = new XML(<customer><name>Fred</name></customer>);
//Example:
var
xml = new XML('<customer><name>Fred</name></customer>');
//Example:
var
w3c = resources.MY_XML.getDocument('XML_DOCUMENT');
var
xml = new XML(w3c);
There
are certain rules that apply when writing Literal XML:
To create an XML element containing multiple elements with the same name, an XMLList object is created. These can be created by writing the XML object directly by wrapping the XML within <> and </> tags or creating a new XMLList object.
//write XMLList directly
var parts = <>
<part
id="AA101" quantity="1"/>
<part
id="BB102" quantity="2"/>
<part
id="CC103" quantity="1"/>
</>
//or write XML using new XMLList()
var parts = new XMLList("<part id='AA101'
quantity='1'/><part id='BB102' quantity='2'/><part id='CC103'
quantity='1'/>");
The XMLList Object can be constructed with:
//Example:
var
xml = new
XMLList(<><name>Fred</name><name>Rachel</name></>);
//Example:
var
xml = new
XMLList('<name>Fred</name><name>Rachel</name>');
Once an XML object is
instantiated, properties and methods can accessed, for example, its name and
namespace, children, attributes and text content using the familiar Javascript
dot operator or by using the E4X API.
//create
customer
var
c = <customer number="1721">
<name>
<first>John</first>
<last>Smith</last>
</name>
<phone type="mobile">888-555-1212</phone>
<phone type="office">888-555-2121</phone>
</customer>;
//Create
name var
var
name = c.name.first + " " + c.name.last;
//get
customer number attribute
var
num = c.@number;
//find
first phone number.
var
firstphone = c.phone[0];
A child element can be accessed by name as a property of the parent. If there is more than one child with that name, an XML List is returned, which can be further qualified by an index or other qualifier. A child attribute can be accessed by name using the "@" prefix, or with the attribute() method.
The text value of an element with a primitive value (no element children) or of an attribute can be obtained explicitly as a string by the toString() method, but in most circumstances the toString() method will be called automatically when a string value is needed. For a complex element (one with children), toString() returns the XML syntax representation of the element. To obtain the XML syntax representation of a node explicitly (including elements with primitive values), use the toXMLString() method.
c.name.toString() |
'<name><first>John</first><last>Smith</last></name>' |
c.name.toXMLString() |
'<name><first>John</first><last>Smith</last></name>' |
c.@number.toString() |
'1721' |
c.@number.toXMLString() |
'1721' |
c.phone[0].toString() |
'888-555-1212' |
c.phone[0].toXMLString() |
'<phone
type="mobile">888-555-1212</phone>' |
A useful construct in XPath is the predicate notation (e.g. "customer/phone[@type='mobile']") to filter a node list. E4X has a similar construct:
c.phone.(@type == "mobile")
In this notation the XML List of elements matching c.phone is filtered to those with a type attribute with the value "mobile". If no elements match, the result is an empty XML list, best checked for empty using ".length() == 0".
The following summary provides some XPath equivalents for common XML navigation operations:
XPath |
Meaning |
E4X Equivalent |
element/* |
Select all children of element |
element.* |
element/@* |
Select all attributes of element |
element.@* |
element//descendent |
Select all descendants (children, grandchildren, etc.) of
element |
element..descendent |
.. or parent::element |
Select the parent of element |
element.parent() |
xmlns:foo="..." element/foo:bar |
Select the foo:bar child of element where foo is the
prefix of a declared namespace |
var foo = new Namespace(...); element.foo::bar |
name(element) |
Return the full name (including prefix if any) of element |
element.name() |
local-name(element) |
Return the local name of element |
element.localName() |
namespace-uri(element) |
Return the namespace uri (if any) of element |
element.namespace() |
element/namespace::* |
Return the collection of namespaces as an Array of
Namespace objects (E4X) or a node set of Namespaces nodes (XPath) |
element.inScopeNamespaces() |
element/processing-instructions(name) |
Return the processing instruction children of element
with the specified name (if omitted, all are returned). |
element.processingInstructions(name) |
string(element) |
Return the concatenated text nodes of this element and all
its descendants |
stringValue(element); stringValue.visible = false; function stringValue(node) { var value = ""; if (node.hasSimpleContent()) { value = node.toString(); } else { for each (var c in node.children()) { value += stringValue(c); } } return value; } |
To iterate
through the matching elements:
//create
customer
var
c = <customer number="1721">
<name>
<first>John</first>
<last>Smith</last>
</name>
<phone
type="mobile">888-555-1212</phone>
<phone
type="office">888-555-2121</phone>
</customer>
;
for
(var i = 0; i < customer.phone.length(); i++){
log(customer.phone[i].toString());
}
This is using identical syntax to that
used to access numbered items in an array. Despite these similarities to
regular arrays, XMLList does not support Array methods such as forEach, and Array
generics such as Array.forEach() are not compatible with XMLList objects.
We can also use the for….each statement introduced in Javascript 1.6 as part of Javascript's E4X support:
for
each (var phone in customer.phone) {
log(phone);
}
Besides the literal use of XML as a value, or parsing XML text into an XML value, E4X provides a templating mechanism to construct complex XML structures from variable and expression values. Within text content, curly braces can be used to insert a value into the XML:
var
nextId = 1234;
var
first = "John";
var
last = "Smith";
var
c = <customer number={nextId++}>
<name>
<first>{first}</first>
<last>{last}</last>
</name>
</customer>;
Attribute values can be determined by replacing the whole attribute value (including quotes!) with an expression. Curly braces within quotes will be treated literally.
Element and attribute names can be evaluated too:
var
phonetype = "mobile";
var
identifiertype = "id";
var
c = <{phonetype} {identifiertype}={nextId++}
/>888-555-2112</{phonetype}>;
XML lists can be created by using the addition operator on individual XML elements:
var
employees = <employee name="Joe"/> +
<employee
name="Arun"/> +
<employee
name="Betty"/>;
The value of an element or attribute can be changed by assigning a new value to it:
c.@number
= 1235;
c.phone.(@type='mobile') = "650-555-1234";
Deleting an XML element or attribute from a structure is accomplished with the delete operator:
delete
c.phone.(@type='office');
To add a child, one can use the += operator to insert a new element at a particular location:
c.phone
+= <phone type='home'>650-555-1414</phone>; // append new phone
child.
c.phone[0]
+= <phone type='home'>650-555-1414</phone>; // insert new phone
child after the first phone child.
The code-assist in the Javascript editor supports the completions for the 5 main XML objects described above. Details for the full API can be found in the ECMA-357 document.
If a variable is assigned using XML directly, then this is assumed to be of type XML as this is the most generic XML type.
It is possible to access an XML document directly through the XML Resource API Javadoc using the following two methods:
These
methods provide a convenient way to get or set a document within an XML
resource and provide an alternative to the use of field mappings.
In the example below, an XML document is read from a file and then the data is extracted directly from the XML resource.
Consider reading the following XML structure from a file:
<?xml version="1.0"
encoding="UTF-8"?>
<customer>
<contact>
<name>Joe Blogs</name>
<age>78</age>
<telephone>077-799-8756</telephone>
</contact>
</customer>
The following Javascript code reads the XML Document and extracts the name, age and telephone elements from the XML.
importPackage(com.ebasetech.xi.api);
importPackage(com.ebasetech.xi.services);
//read the XML Document
resources.XML_EXAMPLE.read();
//get the XML Document from the XML resource
and assign to variable - customer
var customer =
resources.XML_EXAMPLE.getDocument("MY_DOC");
//create a new Javascript XML type
var xmlCustomer = new XML(customer);
//get the name, age and telephone values from
the Customer XML type
var name = xmlCustomer.contact.name;
var age = xmlCustomer.contact.age;
var telephone =
xmlCustomer.contact.telephone;
//assign to fields
fields.NAME.value = name;
fields.AGE.value = age;
fields.TELEPHONE.value = telephone;
Note that
the getDocument(documentName) returns a Java W3C Document (org.w3c.Document).
If you are familiar with this Java API, it is also possible to use this
directly instead of constructing a Javascript XML Object. Details of the
org.w3c.Document API is outside the scope of this document.
In the example below, an XML document is added to an XML Resource and written to a file without using field mappings to the XML resource.
Consider writing the following XML structure to a file:
<?xml version="1.0"
encoding="UTF-8"?>
<purchaseOrder>
<item>
<name>Hairspray</name>
<price>0.99</price>
<quantity>3</quantity>
</item>
<item>
<name>Shampoo</name>
<price>1.99</price>
<quantity>1</quantity>
</item>
</purchaseOrder>
The following Javascript code shows how to create an XML Document within a Script and set the XML document on the XML resource.
//create a new purchase order
var purchaseOrder = <purchaseOrder/>;
//iterate rows
var rows = tables.PURCHASE_ORDERS.rows;
//create row index
var index = 0;
while(rows.next())
{
//append new item
to purchaseOrder
var
item = purchaseOrder.appendChild(<item/>).item;
var
name = tables.PURCHASE_ORDERS.ITEM.value;
var
price = tables.PURCHASE_ORDERS.PRICE.value;
var
quantity = tables.PURCHASE_ORDERS.QUANTITY.value;
item[index].appendChild(<name>{name}</name>);
item[index].appendChild(<price>{price}</price>);
item[index].appendChild(<quantity>{quantity}</quantity>);
index++;
//increment row index
}
//set the purchase order document
resources.XML_EXAMPLE.setDocument("MY_DOC",
purchaseOrder);
//write the document
resources.XML_EXAMPLE.write();