Jath is a template language that lets you declaratively transform XML documents into Javascript objects using JSON markup and XPath selectors. The idea is to write some JSON markup that looks like the object that you want to end up with, and then add XPath selectors to tell Jath where the data should come from.
Turn:
<statuses userid="djn">
<status id="1">
<message>Hello</message>
</status>
<status id="3">
<message>Goodbye</message>
</status>
</statuses>
into:
[
{ id: "1", message: "Hello" },
{ id: "3", message: "Goodbye" }
]
using:
var xml = <your AJAXy call here>;
var template = [ "//status", { id: "@id", message: "message" } ];
var result = Jath.parse( template, xml );
Check out samples.html for more examples.
Say we were parsing an XML stream of status updates from a service like Twitter. The data might look something like this:
<statuses userid="djn">
<status id="1">
<message>Hello</message>
</status>
<status id="3">
<message>Goodbye</message>
</status>
<status id="5">
</status>
</statuses>
We want to consume this data on the client in Javascript. Let's start with a template that will create our status object. We want to have the id and the message as attributes of the object, so we start like this:
var template = { id: "", message: "" };
When Jath processes a template, the field names 'id' and 'message' will be used to hold the data in the resulting Javascript objects. Now that we have described the result format, we need to select where the data comes from. This is where XPath comes in. Relative to the tag, the XPath selector for the id is
@id
and the selector for the message is simply
message
So, let's add that to the template:
var template = { id: "@id", message: "message" };
We could tell Jath to process this template as-is, but to be really useful to us we want it to process all of the statuses and return them to us as an array. The array template form in Jath is an array literal with one small difference: the first element of the array is the XPath selector that returns the collection that the array will hold. The second element is the array item template.
So to get the statuses in our example we want to use an XPath selector such as
//status
giving us a template in the form of
var template = [ "//status", { ... } ];
We already know what our item template looks like, so let's insert into the array template form
var template = [ "//status", { id: "@id", message: "message" } ];
Processing the template looks like this
Jath.parse( template, xml )
where xml is an XML document as a Firefox XML DOM object. The resulting Javascript array, expressed in JSON, would look something like the following
var result = [ { id: "1", message: "Hello" }, { id: "3", message: "Goodbye" }, ... ];
Jath does not support anything other than string data right now, hopefully that will change soon.
Literal content may be added to the template by prefixing the value with a semicolon. For example:
var template [ "//status", { id: "@id", message: ":literaldata" } ];
The character used to denote literal data can be changed by assigning a different value to:
JSON.literalChar
Only a single character may be used as a denotator, not a longer string.
Often more complex XML documents will define namespaces to qualify element tag names. When performing Jath queries on namespaced documents you should implement a namespace resolver. Once a namespace resolver has been defined it can be set on Jath as follows:
Jath.resolver = myResolver
There are two ways to implement XPath namespace resolvers:
- write a function that takes a namespace prefix as an argument and returns the namesace uri:
Jath.resolver = function(prefix) {
if(prefix === "foo") {
return "https://beebop.com/"
}
if(prefix === "bar") {
return "https://rocksteady.com/"
}
}
- use the
dom.createNSResolver()
method. As an argument this element takes a dom node containing namespace definitions. For example consider the following xml doc:
<labels xmlns="https://example.com" xmlns:lbl="https://example.com/labelns">
<lbl:label id='ep' added="2003-06-10">
<name>Shredder</name>
<dimension>X</dimension>
</lbl:label>
</labels>
then the resolver could be defined as follows:
Jath.resolver = document.createNSResolver(document.documentElement)
XPath does not tollerate non-null default namespaces. For example:
<labels xmlns="https://teenage.com" >
<label id='ep' added="2003-06-10">
<name>Shredder</name>
<dimension>X</dimension>
</label>
</labels>
One way to get this working is to manually define a default namespace prefix and write a resolver that returns the default namespace uri in response to it:
Jath.resolver = function(prefix) {
if(prefix === "dlt") {
return "https://teenage.com"
}
}
and then prefix your elements with this default prefix in Jath queries:
Jath.parse( {turtle: "//dlt:name"}, dom )
This software is a proof of concept. There are cases that it cannot handle, and it isn't production-ready. It is not well tested, and the code here is probably only sufficient to serve as a reference implementation of the idea.
Jath fully supports Firefox, Safari, Chrome and Windows Script Host. Opera is mostly supported, and IE lacks namespace handling.
- No support for dates or numeric types
Jath is provided under the MIT free software license. See the file LICENSE for the full text.