---
title: JPath - XPath for Javascript
date: "2008-05-29T12:00:00Z"
categories:
- coding
wp_id: 50
---
XPath is a neat way of navigating deep XML structures. It's like using a directory structure. /table//td gets all the TDs somewhere below TABLE.
Usually, you don't need this sort of a thing for data structures, particularly in JavaScript. Something like table.td would already work. But sometimes, it does help to have something like XPath even for data structures, so I built a simple XPath-like processor for Javascript called JPath.
Here are some examples of how it would work:
| jpath(context, "para") | returns context.para |
| jpath(context, "*") | returns all values of context (for both arrays and objects) |
| jpath(context, "para[0]") | returns context.para[0] |
| jpath(context, "para[last()]") | returns context.para[context.para.length] |
| jpath(context, "*/para") | returns context[all children].para |
| jpath(context, "/doc/chapter[5]/section[2]") | returns context.doc.chapter[5].section[2] |
| jpath(context, "chapter//para") | returns all para elements inside context.chapter |
| jpath(context, "//para") | returns all para elements inside context |
| jpath(context, "//olist/item") | returns all olist.item elements inside context |
| jpath(context, ".") | returns the context |
| jpath(context, ".//para") | same as //para |
| jpath(context, "//para/..") | returns the parent of all para elements inside context |
Some caveats:
- This is an implementation of the abbreviated syntax of XPath. You can't use axis::nodetest
- No functions are supported other than last()
- Only node name tests are allowed, no nodetype tests. So you can't do text() and node()
- Indices are zero-based, not 1-based
There are a couple of reasons why this sort of thing is useful.
- Extracting attributes deep down. Suppose you had an array of arrays, and you wanted the first element of each array.
You could do this the long way:
```javascript
for (var list = [], i = 0; i < data.length; i++) {
list.push(data[i][0]);
}
```
... or the short way:
```javascript
$.map(data, function(v) {
return v[1];
});
```
But the best would be something like:
```javascript
jpath(data, "//1");
```
- Ragged data structures. Take for example the results from Google's AJAX feed API.
```json
{"responseData": {
"feed": {
"title": "Digg",
"link": "http://digg.com/",
"author": "",
"description": "Digg",
"type": "rss20",
"entries": [
{
"title": "The Pirate Bay Moves Servers to Egypt Due to Copyright Laws",
"link": "http://digg.com/tech_news/The_Pirate_Bay_Moves_Servers_to_Egypt_Due_to_Copyright_Laws",
"author": "",
"publishedDate": "Mon, 31 Mar 2008 23:13:33 -0700",
"contentSnippet": "Due to the new copyright legislation that are going ...",
"content": "Due to the new copyright legislation that are going to take...",
"categories": [
]
},
{
"title": "Millions Dead/Dying in Recent Mass-Rick-Rolling by YouTube.",
"link": "http://digg.com/comedy/Millions_Dead_Dying_in_Recent_Mass_Rick_Rolling_by_YouTube",
"author": "",
"publishedDate": "Mon, 31 Mar 2008 22:53:30 -0700",
"contentSnippet": "Click on any \u0022Featured Videos\u0022. When will the insanity stop?",
"content": "Click on any \u0022Featured Videos\u0022. When will the insanity stop?",
"categories": [
]
},
...
]
}
}
, "responseDetails": null, "responseStatus": 200}
```
If you wanted all the title entries, including the feed title, the choice is between:
```javascript
var titles = [ result.feed.title ];
for (var i=0, l=result.feed.entries.length; i... versus...
```javascript
titles = jpath(result, "//title");
```
If, further, you wanted the list of all categories at one shot, you could use:
```javascript
jpath(result, "//categories/*");
```
---
## Comments
- **Will** _7 Mar 2010 1:15 pm_:
See also: http://www.w3schools.com/xpath/tryit.asp?filename=try\_xpath\_select\_pricenodes\_text