1. Toby Inkster
  2. p5-json-path

Commits

Toby Inkster  committed 5f3a58a

JSON-Path implements JSONPath. Should be adding JSON-T for JsonT soon.

  • Participants
  • Branches JSON-Path

Comments (0)

Files changed (13)

File Changes

View file
  • Ignore whitespace
+############################################################################
+## Changes #################################################################
+############################################################################
+
+JSON-Path
+=========
+
+Created:      2010-07-28
+Home page:    <http://search.cpan.org/dist/JSON-Path/>
+Bug tracker:  <http://rt.cpan.org/Dist/Display.html?Queue=JSON-Path>
+Maintainer:   Toby Inkster <mailto:tobyink@cpan.org>
+
+0.080_00 [2010-07-28] # Ported from PHP.
+

File Changes.ttl

View file
  • Ignore whitespace
+#############################################################
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dcs:     <http://ontologi.es/doap-changeset#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
+@prefix my:      <http://purl.org/NET/cpan-uri/dist/JSON-Path/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix toby:    <http://tobyinkster.co.uk/#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+#############################################################
+
+<>
+
+	dc:title         "Changes" ;
+	dc:description   "Revision history for Perl extension JSON::Path."@en ;
+	dc:subject       my:project ;
+	dc:creator       toby:i .
+
+#############################################################
+
+my:v_0-080_00
+
+	a               :Version ;
+	dc:issued       "2010-07-28"^^xsd:date ;
+	:revision       "0.080_00"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/JSON-Path-0.080_00.tar.gz> ;
+	rdfs:label      "Ported from PHP."@en .
+
+#############################################################
+
+my:project
+
+	a               :Project ;
+	:name           "JSON-Path" ;
+	:shortdesc      "search nested hashref/arrayref structures using JSONPath"@en ;
+	:programming-language  "Perl" ;
+	:homepage       <http://search.cpan.org/dist/JSON-Path/> ;
+	:download-page  <http://search.cpan.org/dist/JSON-Path/> ;
+	:bug-database   <http://rt.cpan.org/Dist/Display.html?Queue=JSON-Path> ;
+	:repository     [ a :SVNRepository ; :browse <http://goddamn.co.uk/viewvc/perlmods/JSON-Path/> ] ;
+	:maintainer     toby:i ;
+	:developer      toby:i ;
+	:documenter     toby:i ;
+	:tester         toby:i ;
+	:created        "2010-07-28"^^xsd:date ;
+	:license        <http://dev.perl.org/licenses/> ;
+	:release        my:v_0-080_00 .
+
+#############################################################
+
+toby:i
+
+	a               foaf:Person ;
+	foaf:name       "Toby Inkster" ;
+	foaf:homepage   <http://tobyinkster.co.uk/> ;
+	foaf:page       <http://search.cpan.org/~tobyink/> ;
+	foaf:mbox       <mailto:tobyink@cpan.org> ;
+	<http://www.w3.org/2002/07/owl#sameAs> <http://purl.org/NET/cpan-uri/person/tobyink> .
+
+#############################################################

File Changes.xml

View file
  • Ignore whitespace
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:dbug="http://ontologi.es/doap-bugs#" xmlns:dc="http://purl.org/dc/terms/" xmlns:dcs="http://ontologi.es/doap-changeset#" xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
+<rdf:Description rdf:nodeID="r2F1444D89A7E11DFA5FDD8027C87BD8Dr0">
+	<doap:browse rdf:resource="http://goddamn.co.uk/viewvc/perlmods/JSON-Path/"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#SVNRepository"/>
+</rdf:Description>
+<rdf:Description rdf:about="file:///home/tai/src/perlmods/JSON-Path/Changes.ttl">
+	<dc:creator rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<dc:description xml:lang="en">Revision history for Perl extension JSON::Path.</dc:description>
+	<dc:subject rdf:resource="http://purl.org/NET/cpan-uri/dist/JSON-Path/project"/>
+	<dc:title>Changes</dc:title>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/JSON-Path/project">
+	<doap:bug-database rdf:resource="http://rt.cpan.org/Dist/Display.html?Queue=JSON-Path"/>
+	<doap:created rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2010-07-28</doap:created>
+	<doap:developer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:documenter rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:download-page rdf:resource="http://search.cpan.org/dist/JSON-Path/"/>
+	<doap:homepage rdf:resource="http://search.cpan.org/dist/JSON-Path/"/>
+	<doap:license rdf:resource="http://dev.perl.org/licenses/"/>
+	<doap:maintainer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:name>JSON-Path</doap:name>
+	<doap:programming-language>Perl</doap:programming-language>
+	<doap:release rdf:resource="http://purl.org/NET/cpan-uri/dist/JSON-Path/v_0-080_00"/>
+	<doap:repository rdf:nodeID="r2F1444D89A7E11DFA5FDD8027C87BD8Dr0"/>
+	<doap:shortdesc xml:lang="en">search nested hashref/arrayref structures using JSONPath</doap:shortdesc>
+	<doap:tester rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Project"/>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/JSON-Path/v_0-080_00">
+	<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2010-07-28</dc:issued>
+	<doap:file-release rdf:resource="http://backpan.cpan.org/authors/id/T/TO/TOBYINK/JSON-Path-0.080_00.tar.gz"/>
+	<doap:revision rdf:datatype="http://www.w3.org/2001/XMLSchema#string">0.080_00</doap:revision>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Version"/>
+	<rdfs:label xml:lang="en">Ported from PHP.</rdfs:label>
+</rdf:Description>
+<rdf:Description xmlns:ns1="http://www.w3.org/2002/07/owl#" rdf:about="http://tobyinkster.co.uk/#i">
+	<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
+	<ns1:sameAs rdf:resource="http://purl.org/NET/cpan-uri/person/tobyink"/>
+	<foaf:homepage rdf:resource="http://tobyinkster.co.uk/"/>
+	<foaf:mbox rdf:resource="mailto:tobyink@cpan.org"/>
+	<foaf:name>Toby Inkster</foaf:name>
+	<foaf:page rdf:resource="http://search.cpan.org/~tobyink/"/>
+</rdf:Description>
+</rdf:RDF>

File JSON-Path-0.080_00.tar.gz

  • Ignore whitespace
Binary file added.

File MANIFEST

View file
  • Ignore whitespace
+Changes
+Changes.ttl
+Changes.xml
+Makefile.PL
+MANIFEST
+MANIFEST.SKIP
+README
+META.yml
+SIGNATURE
+
+t/00sig.t
+t/01basic.t
+
+inc/Module/AutoInstall.pm
+inc/Module/Install/AutoInstall.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/DOAPChangeSets.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Include.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install.pm
+inc/Module/Install/ReadmeFromPod.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+inc/Test/Signature.pm
+
+lib/JSON/Path.pm

File MANIFEST.SKIP

View file
  • Ignore whitespace
+^Makefile$
+^blib/
+^pm_to_blib
+^blibdirs
+\.svn
+^example.*\.pl$
+^[^/]+\.(tar\.gz|tar\.bz2|tgz|tbz2|tbz|zip|tar)$
+^MYMETA..yml

File META.yml

View file
  • Ignore whitespace
+---
+abstract: 'search nested hashref/arrayref structures using JSONPath'
+author:
+  - 'Toby Inkster <tobyink@cpan.org>'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+  Test::More: 0.61
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 0.95'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: JSON-Path
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  Error: 0
+  JSON: 0
+  Scalar::Util: 0
+  common::sense: 0
+  perl: 5.8.0
+resources:
+  bugtracker: http://rt.cpan.org/Dist/Display.html?Queue=JSON-Path
+  homepage: http://search.cpan.org/dist/JSON-Path/
+  license: http://dev.perl.org/licenses/
+  repository: http://goddamn.co.uk/viewvc/perlmods/JSON-Path/
+version: 0.080_00

File Makefile.PL

View file
  • Ignore whitespace
+use strict;
+use warnings;
+
+use inc::Module::Install;
+
+my $dist = 'JSON-Path';
+my $fn   = "lib/$dist.pm"; $fn =~ s#-#/#g;
+
+name                $dist;
+perl_version_from   $fn;
+version_from        $fn;
+abstract_from       $fn;
+readme_from         $fn;
+author              'Toby Inkster <tobyink@cpan.org>';
+license             'perl';
+
+requires            'common::sense'      => 0;
+requires            'Error'              => 0;
+requires            'JSON'               => 0;
+requires            'Scalar::Util'       => 0;
+test_requires       'Test::More'         => '0.61';
+
+resources(
+	'homepage'   => "http://search.cpan.org/dist/$dist/",
+	'repository' => "http://goddamn.co.uk/viewvc/perlmods/$dist/",
+	'bugtracker' => "http://rt.cpan.org/Dist/Display.html?Queue=$dist",
+	);
+	
+write_doap_changes;
+write_doap_changes_xml;
+
+include 'Test::Signature';
+auto_install;
+WriteAll(
+	'meta' => 1,
+	'sign' => 1,
+	);

File README

View file
  • Ignore whitespace
+NAME
+    JSON::Path - search nested hashref/arrayref structures using JSONPath
+
+SYNOPSIS
+     my $data = {
+      "store" => {
+        "book" => [ 
+          { "category" =>  "reference",
+            "author"   =>  "Nigel Rees",
+            "title"    =>  "Sayings of the Century",
+            "price"    =>  8.95,
+          },
+          { "category" =>  "fiction",
+            "author"   =>  "Evelyn Waugh",
+            "title"    =>  "Sword of Honour",
+            "price"    =>  12.99,
+          },
+          { "category" =>  "fiction",
+            "author"   =>  "Herman Melville",
+            "title"    =>  "Moby Dick",
+            "isbn"     =>  "0-553-21311-3",
+            "price"    =>  8.99,
+          },
+          { "category" =>  "fiction",
+            "author"   =>  "J. R. R. Tolkien",
+            "title"    =>  "The Lord of the Rings",
+            "isbn"     =>  "0-395-19395-8",
+            "price"    =>  22.99,
+          },
+        ],
+        "bicycle" => [
+          { "color": "red",
+            "price": 19.95,
+          },
+        ],
+      },
+     };
+ 
+     # All authors of books in the store
+     my $jpath   = JSON::Path->new('$.store.book[*].author');
+     my @authors = $jpath->values($data);
+ 
+     # The author of the last (by order) book
+     my $jpath     = JSON::Path->new('$..book[-1:]');
+     my ($tolkien) = $jpath->values($data);
+
+DESCRIPTION
+    This module implements JSONPath, an XPath-like language for searching
+    JSON-like structures.
+
+    JSONPath is described at <http://goessner.net/articles/JsonPath/>.
+
+  Constructor
+    "JSON::Path->new($string)"
+        Given a JSONPath expression $string, returns a JSON::Path object.
+
+  Methods
+    "values($object)"
+        Evaluates the JSONPath expression against an object. The object
+        $object can be either a nested Perl hashref/arrayref structure, or a
+        JSON string capable of being decoded by JSON::from_json.
+
+        Returns a list of structures from within $object which match against
+        the JSONPath expression.
+
+        This method will sometimes throw an error using the Error module.
+
+    "paths($object)"
+        As per "values" but instead of returning structures which match the
+        expression, returns paths that point towards those structures.
+
+    "to_string"
+        Returns the original JSONPath expression as a string.
+
+        This method is usually not needed, as the JSON::Path should
+        automatically stringify itself as appropriate. i.e. the following
+        works:
+
+         my $jpath = JSON::Path->new('$.store.book[*].author');
+         print "I'm looking for: " . $jpath . "\n";
+
+  JSONPath Embedded Perl Expressions
+    JSONPath expressions may contain subexpressions that are evaluated using
+    the native host language. e.g.
+
+     $..book[?($_->{author} =~ /tolkien/i)]
+
+    The stuff between "?(" and ")" is a Perl expression that must return a
+    boolean, used to filter results. As arbitrary Perl may be used, this is
+    clearly quite dangerous unless used in a controlled environment. Thus,
+    it's disabled by default. To enable, set:
+
+     $JSON::Path::Safe = 0;
+
+    There are some differences between the JSONPath spec and this
+    implementation.
+
+    *   JSONPath uses a variable '$' to refer to the root node. This is not
+        a legal variable name in Perl, so '$root' is used instead.
+
+    *   JSONPath uses a variable '@' to refer to the current node. This is
+        not a legal variable name in Perl, so '$_' is used instead.
+
+BUGS
+    Please report any bugs to <http://rt.cpan.org/>.
+
+SEE ALSO
+    Specification: <http://goessner.net/articles/JsonPath/>.
+
+    Implementations in PHP, Javascript and C#:
+    <http://code.google.com/p/jsonpath/>.
+
+    Related modules: JSON, JSON::T.
+
+AUTHOR
+    Toby Inkster <tobyink@cpan.org>.
+
+    This module is pretty much a straight line-by-line port of the PHP
+    JSONPath implementation (version 0.8.x) by Stefan Goessner. See
+    <http://code.google.com/p/jsonpath/>.
+
+COPYRIGHT AND LICENCE
+    Copyright 2007 Stefan Goessner.
+
+    Copyright 2010 Toby Inkster.
+
+  a.k.a. "The MIT Licence"
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be included
+    in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

File example1.pl

View file
  • Ignore whitespace
+use 5.010;
+use lib "lib";
+use JSON;
+use JSON::Path;
+
+my $object = from_json(<<'JSON');
+{ "store": {
+    "book": [ 
+      { "category": "reference",
+        "author": "Nigel Rees",
+        "title": "Sayings of the Century",
+        "price": 8.95
+      },
+      { "category": "fiction",
+        "author": "Evelyn Waugh",
+        "title": "Sword of Honour",
+        "price": 12.99
+      },
+      { "category": "fiction",
+        "author": "Herman Melville",
+        "title": "Moby Dick",
+        "isbn": "0-553-21311-3",
+        "price": 8.99
+      },
+      { "category": "fiction",
+        "author": "J. R. R. Tolkien",
+        "title": "The Lord of the Rings",
+        "isbn": "0-395-19395-8",
+        "price": 22.99
+      }
+    ],
+    "bicycle": {
+      "color": "red",
+      "price": 19.95
+    }
+  }
+}
+JSON
+
+$JSON::Path::Safe = 0;
+
+foreach ('$.store.book[0].title', '$.store.book[*].author', '$..author', '$..book[-1:]',
+	'$..book[?($_->{author} =~ /tolkien/i)]')
+{
+	my $jpath = JSON::Path->new($_);
+	say $jpath;
+	print to_json([$jpath->values($object)], {pretty=>1});
+	print to_json([$jpath->paths($object)], {pretty=>1});
+	say '-' x 40; 
+}

File lib/JSON/Path.pm

View file
  • Ignore whitespace
+package JSON::Path;
+
+use 5.008;
+use common::sense;
+use overload '""' => \&to_string;
+
+our $VERSION = '0.080_00';
+our $Safe    = 1;
+
+use Error qw[:try];
+use JSON qw[from_json];
+
+sub new
+{
+	my ($class, $expression) = @_;
+	return bless \$expression, $class;
+}
+
+sub to_string
+{
+	my ($self) = @_;
+	return $$self;
+}
+
+sub _get
+{
+	my ($self, $object, $type) = @_;
+	$object = from_json($object) unless ref $object;
+	
+	my $helper = JSON::Path::Helper->new;
+	$helper->{'resultType'} = $type;
+	my $norm = $helper->normalize($$self);
+	$helper->{'obj'} = $object;
+	if ($$self && $object)
+	{
+		$norm =~ s/^\$;//;
+		$helper->trace($norm, $object, '$');
+		if (@{ $helper->{'result'} })
+		{
+			return @{ $helper->{'result'} };
+		}
+	}
+	
+	return;
+}
+
+sub values
+{
+	my ($self, $object) = @_;
+	return $self->_get($object, 'VALUE');
+}
+
+sub paths
+{
+	my ($self, $object) = @_;
+	return $self->_get($object, 'PATH');
+}
+
+1;
+
+package JSON::Path::Helper;
+
+use 5.008;
+use common::sense;
+
+use Error qw[:try];
+use Scalar::Util qw[looks_like_number];
+
+sub new
+{
+	return bless {
+		obj        => undef,
+		resultType => 'VALUE',
+		result     => [],
+		subx       => [],
+		}, $_[0];
+}
+
+sub normalize
+{
+	my ($self, $x) = @_;
+	$x =~ s/[\['](\??\(.*?\))[\]']/_callback_01($self,$1)/eg;
+	$x =~ s/'?\.'?|\['?/;/g;
+	$x =~ s/;;;|;;/;..;/g;
+	$x =~ s/;\$|'?\]|'$//g;
+	$x =~ s/#([0-9]+)/_callback_02($self,$1)/eg;
+	$self->{'result'} = [];   # result array was temporarily used as a buffer
+	return $x;
+}
+
+sub _callback_01
+{
+	my ($self, $m1) = @_;
+	push @{ $self->{'result'} }, $m1;
+	my $last_index = scalar @{ $self->{'result'} } - 1;
+	return "[#${last_index}]";
+}
+
+sub _callback_02
+{
+	my ($self, $m1) = @_;
+	return $self->{'result'}->[$m1];
+}
+
+sub asPath
+{
+	my ($self, $path) = @_;
+	my @x = split /\;/, $path;
+	my $p = '$';
+	my $n = scalar(@x);
+	for (my $i=1; $i<$n; $i++)
+	{
+		$p .= /^[0-9*]+$/ ? ("[".$x[$i]."]") : ("['".$x[$i]."']");
+	}
+	return $p;
+}
+
+sub store
+{
+	my ($self, $p, $v) = @_;
+	push @{ $self->{'result'} }, ( $self->{'resultType'} eq "PATH" ? $self->asPath($p) : $v )
+		if $p;
+	return !!$p;
+}
+
+sub trace
+{
+	my ($self, $expr, $val, $path) = @_;
+	
+	return $self->store($path, $val) unless $expr;
+	
+	my ($loc, $x);
+	{
+		my @x = split /\;/, $expr;
+		$loc  = shift @x;
+		$x    = join ';', @x;
+	}
+	
+	if (ref $val eq 'ARRAY'
+	and looks_like_number($loc)
+	and exists $val->[$loc])
+	{
+		$self->trace($x, $val->[$loc], sprintf('%s;%s', $path, $loc));
+	}
+	# in Perl need to distinguish between arrays and hashes.
+	elsif (ref $val eq 'HASH'
+	and exists $val->{$loc})
+	{
+		$self->trace($x, $val->{$loc}, sprintf('%s;%s', $path, $loc));
+	}
+	elsif ($loc eq '*')
+	{
+		$self->walk($loc, $x, $val, $path, \&_callback_03);
+	}
+	elsif ($loc eq '..')
+	{
+		$self->trace($x, $val, $path);
+		$self->walk($loc, $x, $val, $path, \&_callback_04);
+	}
+	elsif ($loc =~ /\,/)  # [name1,name2,...]
+	{
+		$self->trace($_.';'.$x, $val, $path)
+			foreach split /\,/, $loc;
+	}
+	elsif ($loc =~ /^\(.*?\)$/) # [(expr)]
+	{
+		my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
+		$self->trace($evalx.';'.$x, $val, $path);
+	}
+	elsif ($loc =~ /^\?\(.*?\)$/) # [?(expr)]
+	{
+		# my $evalx = $self->evalx($loc, $val, substr($path, rindex($path,";")+1));
+		$self->walk($loc, $x, $val, $path, \&_callback_05);
+	}
+	elsif ($loc =~ /^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/) # [start:end:step]  phyton slice syntax
+	{
+		$self->slice($loc, $x, $val, $path);
+	}
+}
+
+sub _callback_03
+{
+	my ($self, $m, $l, $x, $v, $p) = @_;
+	$self->trace($m.";".$x,$v,$p);
+}
+
+sub _callback_04
+{
+	my ($self, $m, $l, $x, $v, $p) = @_;
+
+	if (ref $v eq 'ARRAY'
+	and ref($v->[$m]) =~ m'^(ARRAY|HASH)$')
+	{
+		$self->trace("..;".$x, $v->[$m], $p.";".$m);
+	}
+	elsif (ref $v eq 'HASH'
+	and ref($v->{$m}) =~ m'^(ARRAY|HASH)$')
+	{
+		$self->trace("..;".$x, $v->{$m}, $p.";".$m);
+	}
+}
+
+sub _callback_05
+{
+	my ($self, $m, $l, $x, $v, $p) = @_;
+	
+	$l =~ s/^\?\((.*?)\)$/$1/g;
+	
+	my $evalx;
+	if (ref $v eq 'ARRAY')
+	{
+		$evalx = $self->evalx($l, $v->[$m]);
+	}
+	elsif (ref $v eq 'HASH')
+	{
+		$evalx = $self->evalx($l, $v->{$m});
+	}
+	
+	$self->trace($m.";".$x, $v, $p)
+		if $evalx;
+}
+
+sub walk
+{
+	my ($self, $loc, $expr, $val, $path, $f) = @_;
+
+	if (ref $val eq 'ARRAY')
+	{
+		map {
+			$f->($self, $_, $loc, $expr, $val, $path);
+		} 0..scalar @$val;
+	}
+
+	elsif (ref $val eq 'HASH')
+	{
+		map {
+			$f->($self, $_, $loc, $expr, $val, $path);
+		} keys %$val;
+	}
+	
+	else
+	{
+		throw Error::Simple('walk called on non hashref/arrayref value.');
+	}
+}
+
+sub slice
+{
+	my ($self, $loc, $expr, $v, $path) = @_;
+	
+	$loc =~ s/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/$1:$2:$3/;
+	my @s   = split /\:/, $loc;
+	my $len = scalar @$v;
+
+	my $start = $s[0]+0 ? $s[0]+0 : 0;
+	my $end   = $s[1]+0 ? $s[1]+0 : $len;
+	my $step  = $s[2]+0 ? $s[2]+0 : 1;
+
+	$start = ($start < 0) ? max(0,$start+$len) : min($len,$start);
+	$end   = ($end < 0)   ? max(0,$end+$len)   : min($len,$end);
+
+	for (my $i=$start; $i<$end; $i+=$step)
+	{
+		$self->trace($i.";".$expr, $v, $path);
+	}
+}
+
+sub max
+{
+	return $_[0] > $_[1] ? $_[0] : $_[1];
+}
+
+sub min
+{
+	return $_[0] < $_[1] ? $_[0] : $_[1];
+}
+
+sub evalx
+{
+	my ($self, $x, $v, $vname) = @_;
+	
+	throw Error::Simple('non-safe evaluation.')
+		if $JSON::Path::Safe;
+		
+	my $expr = $x;
+	$expr =~ s/\$root/\$self->{'obj'}/g;
+	$expr =~ s/\$_/\$v/g;
+
+	local $@ = undef;
+	my $res = eval $expr;
+	
+	if ($@)
+	{
+		throw Error::Simple("eval failed: $expr");
+	}
+	
+	return $res;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+JSON::Path - search nested hashref/arrayref structures using JSONPath
+
+=head1 SYNOPSIS
+
+ my $data = {
+  "store" => {
+    "book" => [ 
+      { "category" =>  "reference",
+        "author"   =>  "Nigel Rees",
+        "title"    =>  "Sayings of the Century",
+        "price"    =>  8.95,
+      },
+      { "category" =>  "fiction",
+        "author"   =>  "Evelyn Waugh",
+        "title"    =>  "Sword of Honour",
+        "price"    =>  12.99,
+      },
+      { "category" =>  "fiction",
+        "author"   =>  "Herman Melville",
+        "title"    =>  "Moby Dick",
+        "isbn"     =>  "0-553-21311-3",
+        "price"    =>  8.99,
+      },
+      { "category" =>  "fiction",
+        "author"   =>  "J. R. R. Tolkien",
+        "title"    =>  "The Lord of the Rings",
+        "isbn"     =>  "0-395-19395-8",
+        "price"    =>  22.99,
+      },
+    ],
+    "bicycle" => [
+      { "color": "red",
+        "price": 19.95,
+      },
+    ],
+  },
+ };
+ 
+ # All authors of books in the store
+ my $jpath   = JSON::Path->new('$.store.book[*].author');
+ my @authors = $jpath->values($data);
+ 
+ # The author of the last (by order) book
+ my $jpath     = JSON::Path->new('$..book[-1:]');
+ my ($tolkien) = $jpath->values($data);
+
+=head1 DESCRIPTION
+
+This module implements JSONPath, an XPath-like language for searching
+JSON-like structures.
+
+JSONPath is described at L<http://goessner.net/articles/JsonPath/>.
+
+=head2 Constructor
+
+=over 4
+
+=item C<<  JSON::Path->new($string)  >>
+
+Given a JSONPath expression $string, returns a JSON::Path object.
+
+=back
+
+=head2 Methods
+
+=over 4
+
+=item C<<  values($object)  >>
+
+Evaluates the JSONPath expression against an object. The object $object
+can be either a nested Perl hashref/arrayref structure, or a JSON string
+capable of being decoded by JSON::from_json.
+
+Returns a list of structures from within $object which match against the
+JSONPath expression.
+
+This method will sometimes throw an error using the L<Error> module.
+
+=item C<<  paths($object)  >>
+
+As per C<values> but instead of returning structures which match
+the expression, returns paths that point towards those structures.
+
+=item C<<  to_string  >>
+
+Returns the original JSONPath expression as a string.
+
+This method is usually not needed, as the JSON::Path should automatically
+stringify itself as appropriate. i.e. the following works:
+
+ my $jpath = JSON::Path->new('$.store.book[*].author');
+ print "I'm looking for: " . $jpath . "\n";
+
+=back
+
+=head2 JSONPath Embedded Perl Expressions
+
+JSONPath expressions may contain subexpressions that are evaluated
+using the native host language. e.g.
+
+ $..book[?($_->{author} =~ /tolkien/i)]
+
+The stuff between "?(" and ")" is a Perl expression that must return
+a boolean, used to filter results. As arbitrary Perl may be used, this
+is clearly quite dangerous unless used in a controlled environment.
+Thus, it's disabled by default. To enable, set:
+
+ $JSON::Path::Safe = 0;
+
+There are some differences between the JSONPath spec and this
+implementation.
+
+=over 4
+
+=item * JSONPath uses a variable '$' to refer to the root node.
+This is not a legal variable name in Perl, so '$root' is used
+instead.
+
+=item * JSONPath uses a variable '@' to refer to the current node.
+This is not a legal variable name in Perl, so '$_' is used
+instead.
+
+=back
+
+=head1 BUGS
+
+Please report any bugs to L<http://rt.cpan.org/>.
+
+=head1 SEE ALSO
+
+Specification: L<http://goessner.net/articles/JsonPath/>.
+
+Implementations in PHP, Javascript and C#:
+L<http://code.google.com/p/jsonpath/>.
+
+Related modules: L<JSON>, L<JSON::T>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+This module is pretty much a straight line-by-line port of the PHP
+JSONPath implementation (version 0.8.x) by Stefan Goessner.
+See L<http://code.google.com/p/jsonpath/>.
+
+=head1 COPYRIGHT AND LICENCE
+
+Copyright 2007 Stefan Goessner.
+
+Copyright 2010 Toby Inkster.
+
+=head2 a.k.a. "The MIT Licence"
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

File t/00sig.t

View file
  • Ignore whitespace
+use lib 'inc';
+use Test::More tests => 1;
+use Test::Signature;
+signature_ok();

File t/01basic.t

View file
  • Ignore whitespace
+use Test::More tests => 9;
+BEGIN { use_ok('JSON::Path') };
+
+use Error qw[:try];
+
+use JSON;
+my $object = from_json(<<'JSON');
+{ "store": {
+    "book": [ 
+      { "category": "reference",
+        "author": "Nigel Rees",
+        "title": "Sayings of the Century",
+        "price": 8.95
+      },
+      { "category": "fiction",
+        "author": "Evelyn Waugh",
+        "title": "Sword of Honour",
+        "price": 12.99
+      },
+      { "category": "fiction",
+        "author": "Herman Melville",
+        "title": "Moby Dick",
+        "isbn": "0-553-21311-3",
+        "price": 8.99
+      },
+      { "category": "fiction",
+        "author": "J. R. R. Tolkien",
+        "title": "The Lord of the Rings",
+        "isbn": "0-395-19395-8",
+        "price": 22.99
+      }
+    ],
+    "bicycle": {
+      "color": "red",
+      "price": 19.95
+    }
+  }
+}
+JSON
+
+my $path1 = JSON::Path->new('$.store.book[0].title');
+is("$path1", '$.store.book[0].title', "overloaded stringification");
+
+my @results1 = $path1->values($object);
+is($results1[0], 'Sayings of the Century', "basic value result");
+
+@results1 = $path1->paths($object);
+is($results1[0], "\$['store']['book']['0']['title']", "basic path result");
+
+my $path2 = JSON::Path->new('$..book[-1:]');
+my ($results2) = $path2->values($object);
+
+is(ref $results2, 'HASH', "hashref value result");
+is($results2->{isbn}, "0-395-19395-8", "hashref seems to be correct");
+
+ok($JSON::Path::Safe, "safe by default");
+
+try {
+	my $path3 = JSON::Path->new('$..book[?($_->{author} =~ /tolkien/i)]');
+	my $results3 = $path3->values($object);
+}
+catch Error::Simple with {
+	ok(1, "disallow dangerous eval");
+}
+
+$JSON::Path::Safe = 0;
+
+my $path3 = JSON::Path->new('$..book[?($_->{author} =~ /tolkien/i)]');
+my ($results3) = $path3->values($object);
+
+is(ref $results3, 'HASH', "dangerous hashref value result");
+is($results3->{isbn}, "0-395-19395-8", "dangerous hashref seems to be correct");