bruce-jsonpath 0.8.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/.document +3 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.markdown +102 -0
- data/Rakefile +66 -0
- data/VERSION +1 -0
- data/lib/jsonpath/nodes.rb +213 -0
- data/lib/jsonpath/parser.rb +1854 -0
- data/lib/jsonpath/parser.treetop +106 -0
- data/lib/jsonpath.rb +22 -0
- data/test/parser_test.rb +180 -0
- data/test/reference_test.rb +136 -0
- data/test/test_helper.rb +37 -0
- metadata +77 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
grammar JSONPathGrammar
|
2
|
+
|
3
|
+
rule path
|
4
|
+
root selectors:child+ <JSONPath::Nodes::RootNode>
|
5
|
+
end
|
6
|
+
|
7
|
+
rule root
|
8
|
+
'$'
|
9
|
+
end
|
10
|
+
|
11
|
+
rule number
|
12
|
+
'-'? [0-9]+
|
13
|
+
end
|
14
|
+
|
15
|
+
rule wildcard
|
16
|
+
'*' / '\'' '*' '\''
|
17
|
+
end
|
18
|
+
|
19
|
+
rule lower
|
20
|
+
descendant / '.'
|
21
|
+
end
|
22
|
+
|
23
|
+
rule descendant
|
24
|
+
'..'
|
25
|
+
end
|
26
|
+
|
27
|
+
rule word_list
|
28
|
+
quoted_word ',' ' '* word_list
|
29
|
+
/
|
30
|
+
quoted_word
|
31
|
+
end
|
32
|
+
|
33
|
+
rule quoted_word
|
34
|
+
'\'' key:(!'\'' . )+ '\''
|
35
|
+
end
|
36
|
+
|
37
|
+
rule child
|
38
|
+
# .foo or ..foo
|
39
|
+
lower key:bareword <JSONPath::Nodes::KeyNode>
|
40
|
+
/
|
41
|
+
# .* or ..*
|
42
|
+
lower wildcard <JSONPath::Nodes::WildcardNode>
|
43
|
+
/
|
44
|
+
# '?(@ % 2 == 0)'
|
45
|
+
lower '\'?(' template_code:(!')\'' . )+ ')\'' <JSONPath::Nodes::FilterNode>
|
46
|
+
/
|
47
|
+
# .'foo'
|
48
|
+
lower '\'' key:(!'\'' . )+ '\'' <JSONPath::Nodes::KeyNode>
|
49
|
+
/
|
50
|
+
# [*] and ['*']
|
51
|
+
'[' wildcard ']' <JSONPath::Nodes::WildcardNode>
|
52
|
+
/
|
53
|
+
# [0]
|
54
|
+
'[' index:number ']' <JSONPath::Nodes::IndexNode>
|
55
|
+
/
|
56
|
+
# .[0] and ..[0]
|
57
|
+
lower '[' index:number ']' <JSONPath::Nodes::IndexNode>
|
58
|
+
/
|
59
|
+
# [1:2]
|
60
|
+
'[' start:number ':' stop:number ']' <JSONPath::Nodes::SliceNode>
|
61
|
+
/
|
62
|
+
# .[1:2] and ..[1:2]
|
63
|
+
lower '[' start:number ':' stop:number ']' <JSONPath::Nodes::SliceNode>
|
64
|
+
/
|
65
|
+
# [1:]
|
66
|
+
'[' start:number ':]' <JSONPath::Nodes::SliceNode>
|
67
|
+
/
|
68
|
+
# .[1:] and ..[1:]
|
69
|
+
lower '[' start:number ':]' <JSONPath::Nodes::SliceNode>
|
70
|
+
/
|
71
|
+
# [1:4:2]
|
72
|
+
'[' start:number ':' stop:number ':' step:number ']' <JSONPath::Nodes::SliceNode>
|
73
|
+
/
|
74
|
+
# .[1:4:2] and ..[1:4:2]
|
75
|
+
lower '[' start:number ':' stop:number ':' step:number ']' <JSONPath::Nodes::SliceNode>
|
76
|
+
/
|
77
|
+
# [1::2]
|
78
|
+
'[' start:number '::' step:number ']' <JSONPath::Nodes::SliceNode>
|
79
|
+
/
|
80
|
+
# .[1::2] and ..[1::2]
|
81
|
+
lower '[' start:number '::' step:number ']' <JSONPath::Nodes::SliceNode>
|
82
|
+
/
|
83
|
+
# [(@.length - 1)]
|
84
|
+
'[(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::ExprNode>
|
85
|
+
/
|
86
|
+
# .[(@.length - 1)] and ..[(@.length - 1)]
|
87
|
+
lower '[(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::ExprNode>
|
88
|
+
/
|
89
|
+
# [?(@ % 2 == 0)]
|
90
|
+
'[?(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::FilterNode>
|
91
|
+
/
|
92
|
+
# .[?(@ % 2 == 0)] and ..[?(@ % 2 == 0)]
|
93
|
+
lower '[?(' template_code:(!')]' . )+ ')]' <JSONPath::Nodes::FilterNode>
|
94
|
+
/
|
95
|
+
# ['foo']
|
96
|
+
'[' word_list ']' <JSONPath::Nodes::KeyNode>
|
97
|
+
/
|
98
|
+
# .['foo'] and ..['foo]
|
99
|
+
lower '[' word_list ']' <JSONPath::Nodes::KeyNode>
|
100
|
+
end
|
101
|
+
|
102
|
+
rule bareword
|
103
|
+
[a-zA-Z0-9]+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/lib/jsonpath.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'treetop'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
require 'jsonpath/parser'
|
6
|
+
require 'jsonpath/nodes'
|
7
|
+
|
8
|
+
module JSONPath
|
9
|
+
|
10
|
+
Parser = ::JSONPathGrammarParser
|
11
|
+
class ParseError < ::SyntaxError; end
|
12
|
+
|
13
|
+
def self.lookup(obj, path)
|
14
|
+
parser = Parser.new
|
15
|
+
if (result = parser.parse(path))
|
16
|
+
result.walk(obj)
|
17
|
+
else
|
18
|
+
raise ParseError, parser.failure_reason
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/parser_test.rb
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
# The content below was taken from the tests for the JS and PHP
|
4
|
+
# reference implementations at http://code.google.com/p/jsonpath/
|
5
|
+
class ParserTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context 'Traversing' do
|
8
|
+
context "hash" do
|
9
|
+
should "parse bareword child with single terminal" do
|
10
|
+
path = '$.a'
|
11
|
+
assert_resolves({"a" => 1}, path, [1])
|
12
|
+
end
|
13
|
+
should "parse subscripted quoted child with single terminal" do
|
14
|
+
path = "$['a b']"
|
15
|
+
assert_resolves({"a b" => 1}, path, [1])
|
16
|
+
end
|
17
|
+
should "parse subscripted quoted child and chained bareword" do
|
18
|
+
path = "$['a b'].c"
|
19
|
+
assert_resolves({"a b" => {"c" => 1}}, path, [1])
|
20
|
+
end
|
21
|
+
should "parses bare wildcard" do
|
22
|
+
path = "$.*"
|
23
|
+
assert_resolves({"a" => 1, "b" => 2}, path, [1, 2])
|
24
|
+
end
|
25
|
+
should "parse quoted wildcard on hash" do
|
26
|
+
path = "$['*']"
|
27
|
+
assert_resolves({"a" => 1, "b" => 2}, path, [1, 2])
|
28
|
+
end
|
29
|
+
should "parse quoted key outside of brackets" do
|
30
|
+
path = "$.'a b'"
|
31
|
+
assert_resolves({"a b" => 1}, path, [1])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context "array" do
|
35
|
+
should "parse bare wildcard" do
|
36
|
+
path = "$.*"
|
37
|
+
assert_resolves([1, 2, 3], path, [1, 2, 3])
|
38
|
+
end
|
39
|
+
should "parses subscripted quoted wildcard" do
|
40
|
+
path = "$['*']"
|
41
|
+
assert_resolves([1, 2, 3], path, [1, 2, 3])
|
42
|
+
end
|
43
|
+
should "parses through bare wildcard" do
|
44
|
+
path = "$.*.name"
|
45
|
+
assert_resolves([{"name" => 1}, {"name" => 2}, {"name" => 3}], path, [1, 2, 3])
|
46
|
+
end
|
47
|
+
should "parse index to single terminal" do
|
48
|
+
path = "$[1]"
|
49
|
+
assert_resolves(%w(foo bar baz), path, %w(bar))
|
50
|
+
end
|
51
|
+
should "parse index to multiple terminals" do
|
52
|
+
path = "$.*[1].name"
|
53
|
+
assert_resolves({
|
54
|
+
"a" => [1, {"name" => 2}, 3],
|
55
|
+
"b" => [4, {"name" => 5}, 6],
|
56
|
+
"c" => [7, {"name" => 8}, 9],
|
57
|
+
}, path, [2, 5, 8])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "combination wildcards" do
|
61
|
+
should "parses through bare wildcard on array with additional wildcard" do
|
62
|
+
path = "$.*.names.*"
|
63
|
+
assert_resolves([
|
64
|
+
{"names" => %w(foo bar)},
|
65
|
+
{"names" => %w(baz quux)},
|
66
|
+
{"names" => %w(spam eggs)}
|
67
|
+
], path, %w(foo bar baz quux spam eggs))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
context "using slices" do
|
71
|
+
setup {
|
72
|
+
@deep = [
|
73
|
+
{"a" => {"name" => "a1"}, "b" => {"name" => "b1"}},
|
74
|
+
{"c" => {"name" => "c1"}, "d" => {"name" => "d1"}},
|
75
|
+
{"e" => {"name" => "e1"}, "f" => {"name" => "f1"}},
|
76
|
+
{"g" => {"name" => "g1"}, "h" => {"name" => "h1"}},
|
77
|
+
{"i" => {"name" => "i1"}, "j" => {"name" => "j1"}},
|
78
|
+
{"k" => {"name" => "k1"}, "l" => {"name" => "l1"}},
|
79
|
+
{"m" => {"name" => "m1"}, "n" => {"name" => "n1"}},
|
80
|
+
]
|
81
|
+
@shallow = [1, 2, 3, 4, 5, 6]
|
82
|
+
}
|
83
|
+
context "with explicit start and stop" do
|
84
|
+
context "with implicit step" do
|
85
|
+
should "parse to single terminal" do
|
86
|
+
path = '$[2:4]'
|
87
|
+
assert_resolves(@shallow, path, [3, 4, 5])
|
88
|
+
end
|
89
|
+
should "parse to multiple terminals" do
|
90
|
+
path = '$[2:4].*.name'
|
91
|
+
assert_resolves(@deep, path, %w(e1 f1 g1 h1 i1 j1))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
context "with explicit step" do
|
95
|
+
should "parse to single terminal" do
|
96
|
+
path = '$[2:4:2]'
|
97
|
+
assert_resolves(@shallow, path, [3, 5])
|
98
|
+
end
|
99
|
+
should "parse to multiple terminals" do
|
100
|
+
path = '$[2:4:2].*.name'
|
101
|
+
assert_resolves(@deep, path, %w(e1 f1 i1 j1))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
context "with explicit start and implict stop" do
|
106
|
+
context "with implicit step" do
|
107
|
+
should "parse to single terminal" do
|
108
|
+
path = '$[2:]'
|
109
|
+
assert_resolves(@shallow, path, [3, 4, 5, 6])
|
110
|
+
end
|
111
|
+
should "parse to multiple terminals" do
|
112
|
+
path = '$[2:].*.name'
|
113
|
+
assert_resolves(@deep, path, %w(e1 f1 g1 h1 i1 j1 k1 l1 m1 n1))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
context "with explicit step" do
|
117
|
+
should "parse to single terminal" do
|
118
|
+
path = '$[2::2]'
|
119
|
+
assert_resolves(@shallow, path, [3, 5])
|
120
|
+
end
|
121
|
+
should "parse to multiple terminals" do
|
122
|
+
path = '$[2::2].*.name'
|
123
|
+
assert_resolves(@deep, path, %w(e1 f1 i1 j1 m1 n1))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
context "supporting filters in Ruby" do
|
129
|
+
setup do
|
130
|
+
@numbers = [1, 2, 3, 4, 5, 6, 7, 8]
|
131
|
+
@hashes = [
|
132
|
+
{"name" => 'Bruce', "age" => 29},
|
133
|
+
{"name" => "Braedyn", "age" => 3},
|
134
|
+
{"name" => "Jamis", "age" => 2},
|
135
|
+
]
|
136
|
+
end
|
137
|
+
context "when using self-contained single statements" do
|
138
|
+
should "support simple object operations" do
|
139
|
+
path = '$[?(@ % 2 == 0)]'
|
140
|
+
assert_resolves(@numbers, path, [2, 4, 6, 8])
|
141
|
+
end
|
142
|
+
should "support manual object pathing" do
|
143
|
+
path = %($[?(@['age'] % 2 == 0)].name)
|
144
|
+
assert_resolves(@hashes, path, ["Jamis"])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
context "descendants" do
|
150
|
+
setup do
|
151
|
+
@object = {
|
152
|
+
"a" => [1, 2, [3, 4]],
|
153
|
+
"b" => {
|
154
|
+
"c" => 5,
|
155
|
+
"e" => [6, 7]
|
156
|
+
}
|
157
|
+
}
|
158
|
+
end
|
159
|
+
should "be found with wildcard" do
|
160
|
+
path = '$..*'
|
161
|
+
assert_resolves(@object, path, [[1, 2, [3, 4]], 1, 2, [3, 4], 3, 4, {"c" => 5, "e" => [6, 7]}, 5, [6, 7], 6, 7, @object])
|
162
|
+
end
|
163
|
+
should "be found with deeper key" do
|
164
|
+
path = '$..e'
|
165
|
+
assert_resolves(@object, path, [[6, 7]])
|
166
|
+
end
|
167
|
+
should "be found with deeper index" do
|
168
|
+
path = '$..[0]'
|
169
|
+
assert_resolves(@object, path, [1, 3, 6])
|
170
|
+
end
|
171
|
+
should "resolve deeper chained selectors" do
|
172
|
+
path = '$..e[?(@ % 2 == 0)]'
|
173
|
+
assert_resolves(@object, path, [6])
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# The content below was taken from the tests for the JS and PHP
|
5
|
+
# reference implementations at http://code.google.com/p/jsonpath/
|
6
|
+
class ReferenceTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
context 'Sample 1' do
|
9
|
+
setup { @json = %({"a":"a","b":"b","c d":"e"}) }
|
10
|
+
should 'resolve a simple path' do
|
11
|
+
assert_resolves(object, "$.a", ["a"])
|
12
|
+
end
|
13
|
+
should 'resolve a path with quotes in brackets' do
|
14
|
+
assert_resolves(object, "$['a']", ["a"])
|
15
|
+
end
|
16
|
+
should 'resolve a path with a space' do
|
17
|
+
assert_resolves(object, "$.'c d'", ["e"])
|
18
|
+
end
|
19
|
+
should 'resolve a star' do
|
20
|
+
assert_resolves(object, "$.*", ["a", "b", "e"])
|
21
|
+
end
|
22
|
+
should 'resolve a star with quotes in brackets' do
|
23
|
+
assert_resolves(object, "$['*']", ["a", "b", "e"])
|
24
|
+
end
|
25
|
+
should 'resolve a star with quotes' do
|
26
|
+
assert_resolves(object, "$[*]", ["a", "b", "e"])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
context 'Sample 2' do
|
30
|
+
setup { @json = %([1, "2", 3.14, true, null]) }
|
31
|
+
should 'resolve with a number in brackets' do
|
32
|
+
assert_resolves(object, "$[0]", [1])
|
33
|
+
end
|
34
|
+
should 'resolve another number in brackets' do
|
35
|
+
assert_resolves(object, "$[4]", [nil])
|
36
|
+
end
|
37
|
+
should 'resolve a star in brackets' do
|
38
|
+
assert_resolves(object, "$[*]", [1, "2", 3.14, true, nil])
|
39
|
+
end
|
40
|
+
should 'resolve an end slice' do
|
41
|
+
assert_resolves(object, "$[-1:]", [nil])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context 'Sample 3' do
|
45
|
+
setup { @json = %({"points":[{"id": "i1", "x": 4, "y": -5}, {"id": "i2", "x": -2, "y": 2, "z": 1}, {"id": "i3", "x": 8, "y": 3}, {"id": "i4", "x": -6, "y": -1}, {"id": "i5", "x": 0, "y": 2, "z": 1}, {"id": "i6", "x": 1, "y": 4}]}) }
|
46
|
+
should 'resolve correctly' do
|
47
|
+
assert_resolves(object, "$.points[1]", [{"id" => "i2", "x" => -2, "y" => 2, "z" => 1}])
|
48
|
+
end
|
49
|
+
should 'resolve a chained path' do
|
50
|
+
assert_resolves(object, "$.points[4].x", [0])
|
51
|
+
end
|
52
|
+
should 'resolve by attribute match' do
|
53
|
+
assert_resolves(object, "$.points[?(@['id']=='i4')].x", [-6])
|
54
|
+
end
|
55
|
+
should 'resolve a chained path with a star in brackets' do
|
56
|
+
assert_resolves(object, "$.points[*].x", [4, -2, 8, -6, 0, 1])
|
57
|
+
end
|
58
|
+
should 'resolve by attribute operation' do
|
59
|
+
assert_resolves(object, "$['points'][?(@['x']*@['x']+@['y']*@['y'] > 50)].id", ["i3"])
|
60
|
+
end
|
61
|
+
should 'resolve by attribute existence' do
|
62
|
+
assert_resolves(object, "$.points[?(@['z'])].id", ["i2", "i5"])
|
63
|
+
end
|
64
|
+
should 'resolve by length property operation' do
|
65
|
+
assert_resolves(object, "$.points[(@.length-1)].id", ["i6"])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
context 'Sample 4' do
|
69
|
+
setup { @json = %({"menu":{"header":"SVG Viewer","items":[{"id": "Open"}, {"id": "OpenNew", "label": "Open New"}, null, {"id": "ZoomIn", "label": "Zoom In"}, {"id": "ZoomOut", "label": "Zoom Out"}, {"id": "OriginalView", "label": "Original View"}, null, {"id": "Quality"}, {"id": "Pause"}, {"id": "Mute"}, null, {"id": "Find", "label": "Find..."}, {"id": "FindAgain", "label": "Find Again"}, {"id": "Copy"}, {"id": "CopyAgain", "label": "Copy Again"}, {"id": "CopySVG", "label": "Copy SVG"}, {"id": "ViewSVG", "label": "View SVG"}, {"id": "ViewSource", "label": "View Source"}, {"id": "SaveAs", "label": "Save As"}, null, {"id": "Help"}, {"id": "About", "label": "About Adobe CVG Viewer..."}]}}) }
|
70
|
+
should 'resolve testing on attribute' do
|
71
|
+
assert_resolves(object, "$.menu.items[?(@ && @['id'] && !@['label'])].id", ["Open", "Quality", "Pause", "Mute", "Copy", "Help"])
|
72
|
+
end
|
73
|
+
should 'resolve testing on attribute with regular expression' do
|
74
|
+
assert_resolves(object, "$.menu.items[?(@ && @['label'] && @['label'] =~ /SVG/)].id", ["CopySVG", "ViewSVG"])
|
75
|
+
end
|
76
|
+
should 'resolve on negative' do
|
77
|
+
# !nil == true in Ruby
|
78
|
+
assert_resolves(object, "$.menu.items[?(!@)]", [nil, nil, nil, nil])
|
79
|
+
end
|
80
|
+
should 'resolve descendant with number in brackets' do
|
81
|
+
assert_resolves(object, "$..[0]", [{"id" => "Open"}])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
context 'Sample 5' do
|
85
|
+
setup { @json = %({"a":[1, 2, 3, 4],"b":[5, 6, 7, 8]}) }
|
86
|
+
should 'resolve descendant with number in brackets' do
|
87
|
+
assert_resolves(object, "$..[0]", [1, 5])
|
88
|
+
end
|
89
|
+
should 'resolve descendant last items' do
|
90
|
+
assert_resolves(object, "$..[-1:]", [4, 8])
|
91
|
+
end
|
92
|
+
should 'resolve by descendant value' do
|
93
|
+
assert_resolves(object, "$..[?(@.is_a?(Numeric) && @ % 2 == 0)]", [2, 4, 6, 8])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
context 'Sample 6' do
|
97
|
+
setup { @json = %({"lin":{"color":"red","x":2,"y":3},"cir":{"color":"blue","x":5,"y":2,"r":1},"arc":{"color":"green","x":2,"y":4,"r":2,"phi0":30,"dphi":120},"pnt":{"x":0,"y":7}}) }
|
98
|
+
should 'resolve by operation in quotes' do
|
99
|
+
assert_resolves(object, "$.'?(@['color'])'.x", [2, 5, 2])
|
100
|
+
end
|
101
|
+
should 'resolve by multiple quoted values in brackets' do
|
102
|
+
assert_resolves(object, "$['lin','cir'].color", ["red", "blue"])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
context 'Sample 7' do
|
106
|
+
setup { @json = %({"text":["hello", "world2.0"]}) }
|
107
|
+
should 'resolve correctly filter 1' do
|
108
|
+
assert_resolves(object, "$.text[?(@.length > 5)]", ["world2.0"])
|
109
|
+
end
|
110
|
+
should 'resolve correctly filter 2' do
|
111
|
+
assert_resolves(object, "$.text[?(@[0, 1] == 'h')]", ["hello"])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
context 'Sample 8' do
|
115
|
+
setup { @json = %({"a":{"a":2,"b":3},"b":{"a":4,"b":5},"c":{"a":{"a":6,"b":7},"c":8}}) }
|
116
|
+
should 'resolve descendant' do
|
117
|
+
assert_resolves(object, "$..a", [{"a" => 2, "b" => 3}, 2, 4, {"a" => 6, "b" => 7}, 6])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
context 'Sample 10' do
|
121
|
+
setup { @json = %({"a":[{"a": 5, "@": 2, "$": 3}, {"a": 6, "@": 3, "$": 4}, {"a": 7, "@": 4, "$": 5}]}) }
|
122
|
+
should 'resolve with quoted operation and escaped special character' do
|
123
|
+
assert_resolves(object, "$.a[?(@['\\@']==3)]", [{"a" => 6, "@" => 3, "$" => 4}])
|
124
|
+
end
|
125
|
+
should 'resolve with quotes and brackets in operation' do
|
126
|
+
assert_resolves(object, "$.a[?(@['$']==5)]", [{"a" => 7, "@" => 4, "$" => 5}])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def object
|
133
|
+
JSON.parse(@json)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
require 'jsonpath'
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def parser
|
14
|
+
@parser ||= JSONPath::Parser.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse(path)
|
18
|
+
parser.parse(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def assert_parses(path)
|
22
|
+
result = parse(path)
|
23
|
+
assert result, parser.failure_reason
|
24
|
+
end
|
25
|
+
|
26
|
+
def assert_resolves(obj, path, result)
|
27
|
+
assert_parses path
|
28
|
+
assert_equal safe_sort(result), safe_sort(parse(path).walk(obj))
|
29
|
+
end
|
30
|
+
|
31
|
+
def safe_sort(objs)
|
32
|
+
objs.sort_by do |obj|
|
33
|
+
obj ? obj.to_s : 0.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bruce-jsonpath
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bruce Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-17 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: treetop
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: bruce@codefluency.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.markdown
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.markdown
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- lib/jsonpath.rb
|
42
|
+
- lib/jsonpath/nodes.rb
|
43
|
+
- lib/jsonpath/parser.rb
|
44
|
+
- lib/jsonpath/parser.treetop
|
45
|
+
- test/parser_test.rb
|
46
|
+
- test/reference_test.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://github.com/bruce/jsonpath
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.2.0
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: JSONPath support for Ruby
|
74
|
+
test_files:
|
75
|
+
- test/parser_test.rb
|
76
|
+
- test/reference_test.rb
|
77
|
+
- test/test_helper.rb
|