/* JPath 1.0.3 - json equivalent to xpath Copyright (C) 2007 Bryan English This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Usage: var jpath = new JPath( myjsonobj ); var somevalue = jpath.$('book/title').json; //results in title //or var somevalue = jpath.query('book/title'); //results in title Supported XPath-like Syntax: /tagname //tagname tagname * wildcard [] predicates operators ( >=, ==, <= ) array selection .. * and, or nodename[0] nodename[last()] nodename[position()] nodename[last()-1] nodename[somenode > 3]/node nodename[count() > 3]/node Tested With: Firefox 2-3, IE 6-7 Update Log: 1.0.1 - Bugfix for zero-based element selection 1.0.2 - Bugfix for IE not handling eval() and returning a function 1.0.3 - Bugfix added support for underscore and dash in query() function Bugfix improper use of Array.concat which was flattening arrays Added support for single equal sign in query() function Added support for count() xpath function Added support for and, or boolean expression in predicate blocks Added support for global selector $$ and // Added support for wildcard (*) selector support */ function JPath( json, parent ) { this.json = json; this._parent = parent; } JPath.prototype = { /* Property: json Copy of current json segment to operate on */ json: null, /* Property: parent Parent json object, null if root. */ parent: null, /* Method: $ Performs a find query on the current jpath object. Parameters: str - mixed, find query to perform. Can consist of a nodename or nodename path or function object or integer. Return: jpath - Returns the resulting jpath object after performing find query. */ '$': function ( str ) { var result = null; var working = this; if ( this.json && str !== null ) { switch ( typeof(str) ) { case 'function': result = this.f(str).json; break; case 'number': result = this.json[str] || null; break; case 'string': var names = str.split('/'); //foreach slash delimited node name// for ( var i=0; i\\<\\!])\\=([^\\=])" : '$1==$2', "\\[" : "$(function(j){ with(j){return(", "\\]" : ");}}).", "\\(\\.":'(', "(\\.|\\])(?!\\$|\\p)":"$1json", "count\\(([^\\)]+)\\)":"count('$1')" }; //save quoted strings// var quotes = /(\'|\")([^\1]*)\1/; var saves = new Array(); while ( quotes.test(str) ) { saves.push( str.match(quotes)[2] ); str = str.replace(quotes,'%'+ (saves.length-1) +'%'); } for ( var e in re ) { str = str.replace( new RegExp(e,'ig'), re[e] ); } //alert('this.' + str.replace(/\%(\d+)\%/g,'saves[$1]') + ";"); return eval('this.' + str.replace(/\%(\d+)\%/g,'saves[$1]') + ";"); }, /* Method: f Performs the equivilant to an xpath predicate eval on the current nodeset. Parameters: f - function, an iterator function that is executed for every json node and is expected to return a boolean value which determines if that particular node is selected. Alternativly you can submit a string which will be inserted into a prepared function. Return: jpath - Returns the resulting jpath object after performing find query. */ f: function ( iterator ) { var a = new Array(); if ( typeof(iterator) == 'string' ) { eval('iterator = function(j){with(j){return('+ iterator +');}}'); } for ( var p in this.json ) { var j = new JPath(this.json[p], this); j.index = p; if ( iterator( j ) ) { a.push( this.json[p] ); } } return new JPath( a, this ); }, /* Method: parent Returns the parent jpath object or itself if its the root node Return: jpath - Returns the parent jpath object or itself if its the root node */ parent: function() { return ( (this._parent) ? this._parent : this ); }, /* Method: position Returns the index position of the current node. Only valid within a function or predicate Return: int - array index position of this json object. */ position: function() { return this.index; }, /* Method: last Returns true if this is the last node in the nodeset. Only valid within a function or predicate Return: booean - Returns true if this is the last node in the nodeset */ last: function() { return (this.index == (this._parent.json.length-1)); }, /* Method: count Returns the count of child nodes in the current node Parameters: string - optional node name to count, defaults to all Return: booean - Returns number of child nodes found */ count: function(n) { var found = this.$( n || '*').json; return ( found ? ( found instanceof Array ? found.length : 1 ) : 0 ); }, /* Method: root Returns the root jpath object. Return: jpath - The top level, root jpath object. */ root: function () { return ( this._parent ? this._parent.root() : this ); } };