jsonpath 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +11 -0
- data/README.txt +46 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/lib/jsonpath.rb +28 -0
- data/lib/jsonpath/expression.rb +97 -0
- data/lib/jsonpath/wrapper.rb +14 -0
- data/spec/jsonpath_spec.rb +88 -0
- data/spec/spec.opts +7 -0
- metadata +64 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
= jsonpath
|
2
|
+
|
3
|
+
== DESCRIPTION:
|
4
|
+
|
5
|
+
* FIX (describe your package)
|
6
|
+
|
7
|
+
== FEATURES/PROBLEMS:
|
8
|
+
|
9
|
+
* FIX (list of features or problems)
|
10
|
+
|
11
|
+
== SYNOPSIS:
|
12
|
+
|
13
|
+
FIX (code sample of usage)
|
14
|
+
|
15
|
+
== REQUIREMENTS:
|
16
|
+
|
17
|
+
* FIX (list of requirements)
|
18
|
+
|
19
|
+
== INSTALL:
|
20
|
+
|
21
|
+
* FIX (sudo gem install, anything else)
|
22
|
+
|
23
|
+
== LICENSE:
|
24
|
+
|
25
|
+
(The MIT License)
|
26
|
+
|
27
|
+
Copyright (c) 2008 FIXME full name
|
28
|
+
|
29
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
30
|
+
a copy of this software and associated documentation files (the
|
31
|
+
'Software'), to deal in the Software without restriction, including
|
32
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
33
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
34
|
+
permit persons to whom the Software is furnished to do so, subject to
|
35
|
+
the following conditions:
|
36
|
+
|
37
|
+
The above copyright notice and this permission notice shall be
|
38
|
+
included in all copies or substantial portions of the Software.
|
39
|
+
|
40
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
41
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
42
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
43
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
44
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
45
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
46
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "jsonpath"
|
7
|
+
s.description = s.summary = "Ruby implementation of http://goessner.net/articles/JsonPath/"
|
8
|
+
s.email = "joshbuddy@gmail.com"
|
9
|
+
s.homepage = "http://github.com/joshbuddy/jsonpath"
|
10
|
+
s.authors = ['Joshua Hull']
|
11
|
+
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
12
|
+
end
|
13
|
+
Jeweler::GemcutterTasks.new
|
14
|
+
rescue LoadError
|
15
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'rake/rdoctask'
|
19
|
+
desc "Generate documentation"
|
20
|
+
Rake::RDocTask.new do |rd|
|
21
|
+
rd.main = "README.rdoc"
|
22
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
23
|
+
rd.rdoc_dir = 'rdoc'
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'spec'
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new do |t|
|
31
|
+
t.spec_opts ||= []
|
32
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
33
|
+
t.spec_files = FileList['spec/*.rb']
|
34
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/jsonpath.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
|
5
|
+
class JsonPath
|
6
|
+
VERSION = '0.0.1'
|
7
|
+
|
8
|
+
def self.path(expression)
|
9
|
+
@expression = Expression.new(expression)
|
10
|
+
if block_given?
|
11
|
+
yield @expression
|
12
|
+
else
|
13
|
+
@expression
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.wrap(object)
|
18
|
+
@wrapper = Wrapper.new(object)
|
19
|
+
if block_given?
|
20
|
+
yield @wrapper
|
21
|
+
else
|
22
|
+
@wrapper
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
require File.join('jsonpath', 'expression')
|
28
|
+
require File.join('jsonpath', 'wrapper')
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
class JsonPath
|
4
|
+
class Expression
|
5
|
+
def initialize(expression, object = nil)
|
6
|
+
scanner = StringScanner.new(expression)
|
7
|
+
@path = []
|
8
|
+
bracket_count = 0
|
9
|
+
while not scanner.eos?
|
10
|
+
token = scanner.scan_until(/($|\$|@|[a-zA-Z]+|\[.*?\]|\.\.|\.(?!\.))/)
|
11
|
+
case token
|
12
|
+
when '.'
|
13
|
+
# do nothing
|
14
|
+
when /^[a-zA-Z]+$/
|
15
|
+
@path << "['#{token}']"
|
16
|
+
else
|
17
|
+
bracket_count == 0 && @path << token or @path[-1] += token
|
18
|
+
bracket_count += token.count('[') - token.count(']')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@object = object
|
22
|
+
end
|
23
|
+
|
24
|
+
def test?(node = @object)
|
25
|
+
each(node) {|n| return true}
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_a(node = @object)
|
30
|
+
store = []
|
31
|
+
each(node) {|o| store << o}
|
32
|
+
store
|
33
|
+
end
|
34
|
+
|
35
|
+
def map(node = @object)
|
36
|
+
store = []
|
37
|
+
each(node) {|o| store << (yield o)}
|
38
|
+
store
|
39
|
+
end
|
40
|
+
|
41
|
+
def each(node = @object, pos = 0, options = {}, &blk)
|
42
|
+
if pos == @path.size
|
43
|
+
return blk.call(node)
|
44
|
+
else
|
45
|
+
case expr = @path[pos]
|
46
|
+
when '*', '..'
|
47
|
+
each(node, pos + 1, &blk)
|
48
|
+
when '$'
|
49
|
+
each(node, pos + 1, &blk) if node == @object
|
50
|
+
when '@'
|
51
|
+
each(node, pos + 1, &blk)
|
52
|
+
when /^\[(.*)\]$/
|
53
|
+
expr[1,expr.size - 2].split(',').each do |sub_path|
|
54
|
+
case sub_path[0]
|
55
|
+
when ?', ?"
|
56
|
+
if node.is_a?(Hash)
|
57
|
+
key = sub_path[1,sub_path.size - 2]
|
58
|
+
each(node[key], pos + 1, &blk) if node.key?(key)
|
59
|
+
end
|
60
|
+
when ??
|
61
|
+
(node.is_a?(Hash) ? node.keys : (0..node.size)).each do |e|
|
62
|
+
::JsonPath.path(sub_path[2,sub_path.size - 3]) do |jp|
|
63
|
+
@obj = node[e]
|
64
|
+
begin
|
65
|
+
each(node[e], pos + 1, &blk) if jp.test?(node[e])
|
66
|
+
rescue
|
67
|
+
# ignore ..
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
else
|
72
|
+
if node.is_a?(Array)
|
73
|
+
@obj = node
|
74
|
+
array_args = sub_path.gsub('@','@obj').split(':')
|
75
|
+
start_idx = (array_args[0] && eval(array_args[0]) || 0) % node.size
|
76
|
+
end_idx = (array_args[1] && eval(array_args[1]) || (sub_path.count(':') == 0 ? start_idx : -1)) % node.size
|
77
|
+
step = array_args[2] && eval(array_args[2]) || 1
|
78
|
+
(start_idx..end_idx).step(step) {|i| each(node[i], pos + 1, &blk)}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
blk.call(node) if pos == (@path.size - 1) && eval("node #{@path[pos]}")
|
84
|
+
end
|
85
|
+
|
86
|
+
if pos > 0 && @path[pos-1] == '..'
|
87
|
+
case node
|
88
|
+
when Hash
|
89
|
+
node.values.each {|n| each(n, pos, &blk) }
|
90
|
+
when Array
|
91
|
+
node.each {|n| each(n, pos, &blk) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require 'lib/jsonpath.rb'
|
3
|
+
require 'json'
|
4
|
+
describe "JsonPath" do
|
5
|
+
|
6
|
+
object = { "store"=> {
|
7
|
+
"book" => [
|
8
|
+
{ "category"=> "reference",
|
9
|
+
"author"=> "Nigel Rees",
|
10
|
+
"title"=> "Sayings of the Century",
|
11
|
+
"price"=> 8.95
|
12
|
+
},
|
13
|
+
{ "category"=> "fiction",
|
14
|
+
"author"=> "Evelyn Waugh",
|
15
|
+
"title"=> "Sword of Honour",
|
16
|
+
"price"=> 12.99
|
17
|
+
},
|
18
|
+
{ "category"=> "fiction",
|
19
|
+
"author"=> "Herman Melville",
|
20
|
+
"title"=> "Moby Dick",
|
21
|
+
"isbn"=> "0-553-21311-3",
|
22
|
+
"price"=> 8.99
|
23
|
+
},
|
24
|
+
{ "category"=> "fiction",
|
25
|
+
"author"=> "J. R. R. Tolkien",
|
26
|
+
"title"=> "The Lord of the Rings",
|
27
|
+
"isbn"=> "0-395-19395-8",
|
28
|
+
"price"=> 22.99
|
29
|
+
}
|
30
|
+
],
|
31
|
+
"bicycle"=> {
|
32
|
+
"color"=> "red",
|
33
|
+
"price"=> 19.95
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
json = JsonPath.wrap(object)
|
38
|
+
|
39
|
+
it "should lookup a direct path" do
|
40
|
+
json.path('$.store.*').to_a.first['book'].size.should == 4
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should retrieve all authors" do
|
44
|
+
json.path('$..author').to_a.should == [
|
45
|
+
object['store']['book'][0]['author'],
|
46
|
+
object['store']['book'][1]['author'],
|
47
|
+
object['store']['book'][2]['author'],
|
48
|
+
object['store']['book'][3]['author']
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should retrieve all prices" do
|
53
|
+
json.path('$..price').to_a.should == [
|
54
|
+
object['store']['bicycle']['price'],
|
55
|
+
object['store']['book'][0]['price'],
|
56
|
+
object['store']['book'][1]['price'],
|
57
|
+
object['store']['book'][2]['price'],
|
58
|
+
object['store']['book'][3]['price']
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should recognize all types of array splices" do
|
63
|
+
json.path('$..book[0:1:1]').to_a.should == [object['store']['book'][0], object['store']['book'][1]]
|
64
|
+
json.path('$..book[1::2]').to_a.should == [object['store']['book'][1], object['store']['book'][3]]
|
65
|
+
json.path('$..book[::2]').to_a.should == [object['store']['book'][0], object['store']['book'][2]]
|
66
|
+
json.path('$..book[:-2:2]').to_a.should == [object['store']['book'][0], object['store']['book'][2]]
|
67
|
+
json.path('$..book[2::]').to_a.should == [object['store']['book'][2], object['store']['book'][3]]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should recognize array comma syntax" do
|
71
|
+
json.path('$..book[0,1]').to_a.should == [object['store']['book'][0], object['store']['book'][1]]
|
72
|
+
json.path('$..book[2,-1::]').to_a.should == [object['store']['book'][2], object['store']['book'][3]]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should support filters" do
|
76
|
+
json.path("$..book[?(@['isbn'])]").to_a.should == [object['store']['book'][2], object['store']['book'][3]]
|
77
|
+
json.path("$..book[?(@['price'] < 10)]").to_a.should == [object['store']['book'][0], object['store']['book'][2]]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should support eval'd array indicies" do
|
81
|
+
json.path('$..book[(@.length-2)]').to_a.should == [object['store']['book'][2]]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should correct retrieve the right number of all nodes" do
|
85
|
+
json.path('$..*').to_a.size.should == 28
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/spec/spec.opts
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsonpath
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joshua Hull
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-25 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby implementation of http://goessner.net/articles/JsonPath/
|
17
|
+
email: joshbuddy@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.txt
|
24
|
+
files:
|
25
|
+
- History.txt
|
26
|
+
- Manifest.txt
|
27
|
+
- README.txt
|
28
|
+
- Rakefile
|
29
|
+
- VERSION
|
30
|
+
- lib/jsonpath.rb
|
31
|
+
- lib/jsonpath/expression.rb
|
32
|
+
- lib/jsonpath/wrapper.rb
|
33
|
+
- spec/jsonpath_spec.rb
|
34
|
+
- spec/spec.opts
|
35
|
+
has_rdoc: true
|
36
|
+
homepage: http://github.com/joshbuddy/jsonpath
|
37
|
+
licenses: []
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options:
|
41
|
+
- --charset=UTF-8
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.5
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Ruby implementation of http://goessner.net/articles/JsonPath/
|
63
|
+
test_files:
|
64
|
+
- spec/jsonpath_spec.rb
|