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:for (var list=[], i=0; i < data.length; i++) { list.push(data[i][0]); }
... or the short way:
$.map(data, function(v) { return v[1]; })
But the best would be something like:
jpath(data, "//1")
- Ragged data structures. Take for example the results from Google's AJAX feed API.
{"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:var titles = [ result.feed.title ]; for (var i=0, l=result.feed.entries.length; i<l; i++) { titles.push(result.feed.entries[i].title; }
... versus...
titles = jpath(result, '//title');
If, further, you wanted the list of all categories at one shot, you could use:
jpath(result, "//categories/*")
See also: http://www.w3schools.com/xpath/tryit.asp?filename=try_xpath_select_pricenodes_text