jsonpath 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +7 -1
- data/bin/jsonpath +3 -3
- data/jsonpath.gemspec +1 -1
- data/lib/jsonpath.rb +4 -0
- data/lib/jsonpath/enumerable.rb +35 -18
- data/lib/jsonpath/proxy.rb +25 -0
- data/lib/jsonpath/version.rb +1 -1
- data/test/test_jsonpath.rb +60 -31
- data/test/test_jsonpath_bin.rb +21 -0
- metadata +8 -6
data/README.md
CHANGED
@@ -47,4 +47,10 @@ As well, you can include it as a library.
|
|
47
47
|
# => 8.95
|
48
48
|
~~~~~
|
49
49
|
|
50
|
-
You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
|
50
|
+
You can optionally prevent eval from being called on sub-expressions by passing in :allow_eval => false to the constructor.
|
51
|
+
|
52
|
+
If you'd like to do substitution in a json object, do this:
|
53
|
+
JsonPath.for({'test' => 'time'}).gsub('$..test') {|v| v << v}
|
54
|
+
|
55
|
+
The result will be
|
56
|
+
{'test' => 'timetime'}
|
data/bin/jsonpath
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/local/env ruby
|
2
2
|
|
3
3
|
require 'jsonpath'
|
4
|
-
require '
|
4
|
+
require 'yajl'
|
5
5
|
|
6
6
|
def usage
|
7
7
|
puts "jsonpath [expression] (file|string)
|
@@ -17,8 +17,8 @@ jsonpath = JsonPath.new(ARGV[0])
|
|
17
17
|
case ARGV[1]
|
18
18
|
when nil #stdin
|
19
19
|
until STDIN.eof?
|
20
|
-
puts jsonpath.on(
|
20
|
+
puts Yajl::Encoder.encode(jsonpath.on(Yajl::Parser.parse(STDIN.readline)))
|
21
21
|
end
|
22
22
|
when String
|
23
|
-
puts jsonpath.on(
|
23
|
+
puts Yajl::Encoder.encode(jsonpath.on(Yajl::Parser.parse(File.exist?(ARGV[1]) ? File.read(ARGV[1]) : ARGV[1])))
|
24
24
|
end
|
data/jsonpath.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
22
|
|
23
23
|
# dependencies
|
24
|
-
s.add_runtime_dependency '
|
24
|
+
s.add_runtime_dependency 'yajl-ruby'
|
25
25
|
s.add_development_dependency 'code_stats'
|
26
26
|
s.add_development_dependency 'rake'
|
27
27
|
s.add_development_dependency 'minitest', '~> 2.2.0'
|
data/lib/jsonpath.rb
CHANGED
data/lib/jsonpath/enumerable.rb
CHANGED
@@ -1,32 +1,37 @@
|
|
1
1
|
class JsonPath
|
2
2
|
class Enumerable
|
3
3
|
include ::Enumerable
|
4
|
+
attr_reader :allow_eval
|
5
|
+
alias_method :allow_eval?, :allow_eval
|
4
6
|
|
5
7
|
def initialize(path, object, options = nil)
|
6
8
|
@path, @object, @options = path.path, object, options
|
9
|
+
@allow_eval = @options && @options.key?(:allow_eval) ? @options[:allow_eval] : true
|
10
|
+
@mode = @options && @options[:mode]
|
7
11
|
end
|
8
12
|
|
9
|
-
def each(
|
10
|
-
|
13
|
+
def each(context = @object, key = nil, pos = 0, &blk)
|
14
|
+
node = key ? context[key] : context
|
15
|
+
return yield_value(blk, context, key) if pos == @path.size
|
11
16
|
case expr = @path[pos]
|
12
17
|
when '*', '..'
|
13
|
-
each(
|
18
|
+
each(context, key, pos + 1, &blk)
|
14
19
|
when '$'
|
15
|
-
each(
|
20
|
+
each(context, key, pos + 1, &blk) if node == @object
|
16
21
|
when '@'
|
17
|
-
each(
|
22
|
+
each(context, key, pos + 1, &blk)
|
18
23
|
when /^\[(.*)\]$/
|
19
24
|
expr[1,expr.size - 2].split(',').each do |sub_path|
|
20
25
|
case sub_path[0]
|
21
26
|
when ?', ?"
|
22
27
|
if node.is_a?(Hash)
|
23
|
-
|
24
|
-
each(node
|
28
|
+
k = sub_path[1,sub_path.size - 2]
|
29
|
+
each(node, k, pos + 1, &blk) if node.key?(k)
|
25
30
|
end
|
26
31
|
when ??
|
27
32
|
(node.is_a?(Hash) ? node.keys : (0..node.size)).each do |e|
|
28
33
|
subenum = ::JsonPath.new(sub_path[2, sub_path.size - 3]).on(node[e])
|
29
|
-
each(node
|
34
|
+
each(node, e, pos + 1, &blk) if subenum.any?{|n| true}
|
30
35
|
end
|
31
36
|
else
|
32
37
|
if node.is_a?(Array)
|
@@ -40,25 +45,41 @@ class JsonPath
|
|
40
45
|
end_idx %= node.size
|
41
46
|
step = process_function_or_literal(array_args[2], 1)
|
42
47
|
next unless step
|
43
|
-
(start_idx..end_idx).step(step) {|i| each(node
|
48
|
+
(start_idx..end_idx).step(step) {|i| each(node, i, pos + 1, &blk)}
|
44
49
|
end
|
45
50
|
end
|
46
51
|
end
|
47
52
|
else
|
48
|
-
|
53
|
+
if pos == (@path.size - 1) && node && allow_eval?
|
54
|
+
if eval("node #{@path[pos]}")
|
55
|
+
yield_value(blk, context, key)
|
56
|
+
end
|
57
|
+
end
|
49
58
|
end
|
50
59
|
|
51
60
|
if pos > 0 && @path[pos-1] == '..'
|
52
61
|
case node
|
53
|
-
when Hash
|
54
|
-
|
55
|
-
when Array
|
56
|
-
node.each {|n| each(n, pos, &blk) }
|
62
|
+
when Hash then node.each {|k, v| each(node, k, pos, &blk) }
|
63
|
+
when Array then node.each_with_index {|n, i| each(node, i, pos, &blk) }
|
57
64
|
end
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
68
|
private
|
69
|
+
def yield_value(blk, context, key)
|
70
|
+
@substitute_with = nil
|
71
|
+
case @mode
|
72
|
+
when nil
|
73
|
+
blk.call(key ? context[key] : context)
|
74
|
+
when :substitute
|
75
|
+
if key
|
76
|
+
context[key] = blk.call(context[key])
|
77
|
+
else
|
78
|
+
context.replace(blk.call(context[key]))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
62
83
|
def process_function_or_literal(exp, default)
|
63
84
|
if exp.nil?
|
64
85
|
default
|
@@ -70,9 +91,5 @@ class JsonPath
|
|
70
91
|
Integer(exp)
|
71
92
|
end
|
72
93
|
end
|
73
|
-
|
74
|
-
def allow_eval?
|
75
|
-
@options.nil? || @options[:allow_eval] != false
|
76
|
-
end
|
77
94
|
end
|
78
95
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class JsonPath
|
2
|
+
class Proxy
|
3
|
+
def initialize(obj)
|
4
|
+
@obj = obj
|
5
|
+
end
|
6
|
+
|
7
|
+
def gsub(path, replacement = nil, &replacement_block)
|
8
|
+
_gsub(deep_copy, path, replacement ? proc{replacement} : replacement_block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def gsub!(path, replacement = nil, &replacement_block)
|
12
|
+
_gsub(@obj, path, replacement ? proc{replacement} : replacement_block)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def deep_copy
|
17
|
+
Marshal::load(Marshal::dump(@obj))
|
18
|
+
end
|
19
|
+
|
20
|
+
def _gsub(obj, path, replacement)
|
21
|
+
JsonPath.new(path, :mode => :substitute).enum_on(obj).each(&replacement)
|
22
|
+
obj
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/jsonpath/version.rb
CHANGED
data/test/test_jsonpath.rb
CHANGED
@@ -1,40 +1,12 @@
|
|
1
1
|
class TestJsonpath < MiniTest::Unit::TestCase
|
2
2
|
|
3
3
|
def setup
|
4
|
-
@object =
|
5
|
-
|
6
|
-
{ "category"=> "reference",
|
7
|
-
"author"=> "Nigel Rees",
|
8
|
-
"title"=> "Sayings of the Century",
|
9
|
-
"price"=> 8.95
|
10
|
-
},
|
11
|
-
{ "category"=> "fiction",
|
12
|
-
"author"=> "Evelyn Waugh",
|
13
|
-
"title"=> "Sword of Honour",
|
14
|
-
"price"=> 12.99
|
15
|
-
},
|
16
|
-
{ "category"=> "fiction",
|
17
|
-
"author"=> "Herman Melville",
|
18
|
-
"title"=> "Moby Dick",
|
19
|
-
"isbn"=> "0-553-21311-3",
|
20
|
-
"price"=> 8.99
|
21
|
-
},
|
22
|
-
{ "category"=> "fiction",
|
23
|
-
"author"=> "J. R. R. Tolkien",
|
24
|
-
"title"=> "The Lord of the Rings",
|
25
|
-
"isbn"=> "0-395-19395-8",
|
26
|
-
"price"=> 22.99
|
27
|
-
}
|
28
|
-
],
|
29
|
-
"bicycle"=> {
|
30
|
-
"color"=> "red",
|
31
|
-
"price"=> 19.95,
|
32
|
-
"catalogue_number" => 12345 }
|
33
|
-
} }
|
4
|
+
@object = example_object
|
5
|
+
@object2 = example_object
|
34
6
|
end
|
35
7
|
|
36
8
|
def test_lookup_direct_path
|
37
|
-
assert_equal 4, JsonPath.new('$.store.*').on(@object).
|
9
|
+
assert_equal 4, JsonPath.new('$.store.*').on(@object).first['book'].size
|
38
10
|
end
|
39
11
|
|
40
12
|
def test_retrieve_all_authors
|
@@ -103,4 +75,61 @@ class TestJsonpath < MiniTest::Unit::TestCase
|
|
103
75
|
assert_equal JsonPath.new('$..author').on(@object), JsonPath.on(@object, '$..author')
|
104
76
|
end
|
105
77
|
|
78
|
+
def test_gsub
|
79
|
+
@object2['store']['bicycle']['price'] += 10
|
80
|
+
@object2['store']['book'][0]['price'] += 10
|
81
|
+
@object2['store']['book'][1]['price'] += 10
|
82
|
+
@object2['store']['book'][2]['price'] += 10
|
83
|
+
@object2['store']['book'][3]['price'] += 10
|
84
|
+
assert_equal @object2, JsonPath.for(@object).gsub('$..price') { |p| p + 10 }
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_gsub!
|
88
|
+
JsonPath.for(@object).gsub!('$..price') { |p| p + 10 }
|
89
|
+
assert_equal 30, @object['store']['bicycle']['price']
|
90
|
+
assert_equal 19, @object['store']['book'][0]['price']
|
91
|
+
assert_equal 23, @object['store']['book'][1]['price']
|
92
|
+
assert_equal 19, @object['store']['book'][2]['price']
|
93
|
+
assert_equal 33, @object['store']['book'][3]['price']
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_weird_gsub!
|
97
|
+
h = {'hi' => 'there'}
|
98
|
+
JsonPath.for(@object).gsub!('$.*') { |n| h }
|
99
|
+
assert_equal h, @object
|
100
|
+
end
|
101
|
+
|
102
|
+
def example_object
|
103
|
+
{ "store"=> {
|
104
|
+
"book" => [
|
105
|
+
{ "category"=> "reference",
|
106
|
+
"author"=> "Nigel Rees",
|
107
|
+
"title"=> "Sayings of the Century",
|
108
|
+
"price"=> 9
|
109
|
+
},
|
110
|
+
{ "category"=> "fiction",
|
111
|
+
"author"=> "Evelyn Waugh",
|
112
|
+
"title"=> "Sword of Honour",
|
113
|
+
"price"=> 13
|
114
|
+
},
|
115
|
+
{ "category"=> "fiction",
|
116
|
+
"author"=> "Herman Melville",
|
117
|
+
"title"=> "Moby Dick",
|
118
|
+
"isbn"=> "0-553-21311-3",
|
119
|
+
"price"=> 9
|
120
|
+
},
|
121
|
+
{ "category"=> "fiction",
|
122
|
+
"author"=> "J. R. R. Tolkien",
|
123
|
+
"title"=> "The Lord of the Rings",
|
124
|
+
"isbn"=> "0-395-19395-8",
|
125
|
+
"price"=> 23
|
126
|
+
}
|
127
|
+
],
|
128
|
+
"bicycle"=> {
|
129
|
+
"color"=> "red",
|
130
|
+
"price"=> 20,
|
131
|
+
"catalogue_number" => 12345 }
|
132
|
+
} }
|
133
|
+
end
|
134
|
+
|
106
135
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class TestJsonpathBin < MiniTest::Unit::TestCase
|
2
|
+
def setup
|
3
|
+
@runner = "ruby -Ilib bin/jsonpath"
|
4
|
+
@original_dir = Dir.pwd
|
5
|
+
Dir.chdir(File.join(File.dirname(__FILE__), '..'))
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
Dir.chdir(@original_dir)
|
10
|
+
`rm /tmp/test.json`
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_stdin
|
14
|
+
assert_equal '["time"]', `echo '{"test": "time"}' | #{@runner} '$.test'`.strip
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_stdin
|
18
|
+
File.open('/tmp/test.json', 'w'){|f| f << '{"test": "time"}'}
|
19
|
+
assert_equal '["time"]', `#{@runner} '$.test' /tmp/test.json`.strip
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonpath
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Hull
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-16 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
22
|
+
name: yajl-ruby
|
23
23
|
prerelease: false
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
@@ -125,8 +125,10 @@ files:
|
|
125
125
|
- jsonpath.gemspec
|
126
126
|
- lib/jsonpath.rb
|
127
127
|
- lib/jsonpath/enumerable.rb
|
128
|
+
- lib/jsonpath/proxy.rb
|
128
129
|
- lib/jsonpath/version.rb
|
129
130
|
- test/test_jsonpath.rb
|
131
|
+
- test/test_jsonpath_bin.rb
|
130
132
|
has_rdoc: true
|
131
133
|
homepage: http://github.com/joshbuddy/jsonpath
|
132
134
|
licenses: []
|