XML API
The Smile CDR Javascript Execution Environment has basic facilities for working with XML documents.
A globally available XML
object is available in JavaScript execution environments.
Given a valid XML string, you can create a parsable XML document object by calling XML.createDocument(str)
.
The resulting XML document can then be modified or traversed with the XPath APIs listed below.
Example:
var xmlDoc = XML.createDocument(stringVersionOfDocument);
Many of the JavaScript operations for working with XML use XPath expressions. XPath is a powerful tool, but it can be tricky to get right. There is an online tool found at http://xpather.com/ that can be helpful in building and testing expressions.
Consider the following document:
<animals:Dog xmlns:animals="http://animals">
<animals:Legs>4</animals:Legs>
</animals:Dog>
This document uses an XML Namespace and prefix to declare the element names Dog
and Legs
. Extracting values from these elements can be done with two different XPath forms:
/*:Dog/*:Legs/text()
/animals:Dog/animals:Legs/text()
This function returns a String containing the output of an XPath expression executed against the document (or an element within the document).
For example, given the document:
<Books>
<Book id='123'/>
</Books>
The following script will output the text "123".
let id = document.getXPathValue('/Books/Book/@id');
Log.info('The ID is: ' + id);
This function returns an Array of Strings containing the output of an XPath expression executed against the document (or an element within the document).
For example, given the document:
<Books>
<Book id='123'/>
<Book id='456'/>
</Books>
The following script will output the text "123".
let ids = document.getXPathValue('/Books/Book/@id');
Log.info('The IDs are: ' + ids);
This function returns an array of XML child elements resulting from the evaluation of an XPath expression executed against the document (or an element within the document). Entries in the array will be XML elements and can have further XML functions applied to them.
For example, given the document:
<Books>
<Book><title>Why Dogs are Better</title></Book>
<Book><title>Why Cats are Better</title></Book>
</Books>
The following script will place the book titles into an array.
let titles = []; // create an empty array
let books = theDocument.getXPathElements('/Books/Book');
for (let index = 0; index < books.length; index++) {
let book = books[index];
let title = book.getXPathValue('title');
titles.push(title);
}
While default XPath namespace resolution is often sufficient for most cases, there may be cases where additional namespace prefixes need to be mapped for correct parsing.
For example, given the document:
<books xmlns="some-url">
<book>Alice in Wonderland</book>
<book>Through the Looking-glass</book>
<abc:book>Green Eggs and Ham</abc:book>
</books>
The following script illustrates adding a custom namespace mapping and using it to parse the document instead.
document.mapNamespacePrefix("a", "some-url");
let books = document.getXPathElements('/a:books/a:book');
NB: It's important to note that this does not add namespaces to the document itself, only allows mapping for existing namespaces.
To add a namespace to the document itself, see addNamespace
below.
When called on a document or child node, it will output the XML string, starting at the provided node.
Allows you to change the inner content of the node.
Eg:
Given the following XML node
<root>Foo</root>
console.log(node.getTextContent()); // outputs "Foo"
node.setTextContent("Bar");
console.log(node.getTextContent()); // outputs "Bar"
The getNode()
function works similar to getXPathElements
and getXPathValue
,
returning a node or value at the defined path.
Unlike getXPathElements
, it will return only a single XML element.
Unlike getXPathValue
, it returns a node (that can be used for update purposes) instead of just the value at that place.
The setNodeValue
function is used to set a given value to an already obtained node.
In most use cases, it's for setting the property value of an obtained attribute node.
Eg:
Given the following XML
<root foo="bar"></root>
// retrieve the "foo" attribute node
const fooNode = document.getNode("@foo");
// change the foo attribute value
// resulting XML: <root foo="unbar"></root>
fooNode.setNodeValue("unbar");
This function can be used on an element-type node to either set or add a new attribute value.
Eg:
Given the XML
<root></root>
console.log(document.toXMLString()); // outputs <root></root>
document.setOrAddAttribute("foo", "bar");
console.log(document.toXMLString()); // outputs <root foo="bar"/>
This function, when used on an element type node, will retrieve all known attributes (property name and property value) on the current node.
Example:
Given the xml
<root xmlns="namespace-url" id="root"></root>
const attrs = document.getNodeAttributes();
console.log(attrs[0].name + " = " + attrs[1].value); // outputs "xmlns = namespace-url"
console.log(attrs[1].name + " = " + attrs[1].value); // outputs "id = root"
The functions appendChild
and removeChild
can be used to add, or remove a node from the document respectively.
While any random new XML can be created by passing an XML string to
XML.createDocument(strXML)
, in order to create a new node on an existing document, the node must have originated from the document in question.
You can create a node associated with an existing document with the
createNewElement(tagName)
function.
Examples:
<books>
<book author="Carroll">Alice in Wonderland</book>
<book author="Orwell">1984</book>
<book author="Seuss">Green Eggs and Ham</book>
</books>
Example 1: Appending a new child node
const booksEl = document.getXPathElements('/books')[0];
const newBook = document.createNewElement('book'); // creates a new <book> element
newBook.setTextContent("Misery");
newBook.setOrAddAttribute("author", "King");
booksEl.appendChild(newBook);
Example 2: Removing an existing child node
const books = document.getXPathElements('/books/book');
const booksEl = document.getXPathElements('/books')[0];
const firstBook = books[0];
booksEl.removeChild(firstBook);
This function can be used to add a new namespace, or alter an existing namespace, on a given element type node.
This function is useful if an element with a different namespace needs to be added to a document that currently has no such namespace mapping.
Example:
<root xmlns="default">
<child>value</child>
</root>
const root = document.getXPathElements('/root')[0];
// add the xmlns:abc namespace to the entire document
document.addNamespace('xmlns:abc', 'value', 'http://www.w3.org/2000/xmlns/');
// now a new child can be created with this namespace
const newChild = document.createNewElement('abc:child');
root.appendChild(newChild);
NB: Namespaces prefixed with xml
and xmlns
must have namespace uris of
http://www.w3.org/XML/1998/namespace
and http://www.w3.org/2000/xmlns/
respectively.