json_p3 0.4.0 → 1.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +26 -7
- data/.ruby-version +1 -1
- data/CHANGELOG.md +58 -0
- data/README.md +125 -123
- data/Rakefile +3 -3
- data/certs/jgrp.pem +21 -21
- data/lib/json_p3/errors.rb +51 -43
- data/lib/json_p3/patch/op.rb +23 -0
- data/lib/json_p3/patch/op_add.rb +51 -0
- data/lib/json_p3/patch/op_copy.rb +64 -0
- data/lib/json_p3/patch/op_move.rb +74 -0
- data/lib/json_p3/patch/op_remove.rb +56 -0
- data/lib/json_p3/patch/op_replace.rb +54 -0
- data/lib/json_p3/patch/op_test.rb +31 -0
- data/lib/json_p3/patch.rb +15 -330
- data/lib/json_p3/path/environment.rb +113 -0
- data/lib/json_p3/path/filter.rb +463 -0
- data/lib/json_p3/path/function.rb +12 -0
- data/lib/json_p3/path/function_extensions/count.rb +15 -0
- data/lib/json_p3/path/function_extensions/length.rb +17 -0
- data/lib/json_p3/path/function_extensions/match.rb +62 -0
- data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
- data/lib/json_p3/path/function_extensions/search.rb +44 -0
- data/lib/json_p3/path/function_extensions/value.rb +15 -0
- data/lib/json_p3/path/lexer.rb +220 -0
- data/lib/json_p3/path/node.rb +48 -0
- data/lib/json_p3/path/parser.rb +676 -0
- data/lib/json_p3/path/query.rb +74 -0
- data/lib/json_p3/path/segment.rb +172 -0
- data/lib/json_p3/path/selector.rb +304 -0
- data/lib/json_p3/path/serialize.rb +16 -0
- data/lib/json_p3/path/unescape.rb +134 -0
- data/lib/json_p3/pointer.rb +15 -76
- data/lib/json_p3/relative_pointer.rb +69 -0
- data/lib/json_p3/version.rb +1 -1
- data/lib/json_p3.rb +50 -13
- data/sig/json_p3/cache.rbs +21 -0
- data/sig/json_p3/errors.rbs +55 -0
- data/sig/json_p3/patch.rbs +145 -0
- data/sig/json_p3/path/environment.rbs +81 -0
- data/sig/json_p3/path/filter.rbs +196 -0
- data/sig/json_p3/path/function.rbs +94 -0
- data/sig/json_p3/path/lexer.rbs +62 -0
- data/sig/json_p3/path/node.rbs +46 -0
- data/sig/json_p3/path/parser.rbs +92 -0
- data/sig/json_p3/path/query.rbs +47 -0
- data/sig/json_p3/path/segment.rbs +54 -0
- data/sig/json_p3/path/selector.rbs +100 -0
- data/sig/json_p3/path/serialize.rbs +9 -0
- data/sig/json_p3/path/unescape.rbs +12 -0
- data/sig/json_p3/pointer.rbs +64 -0
- data/sig/json_p3/relative_pointer.rbs +30 -0
- data/sig/json_p3.rbs +24 -1313
- data.tar.gz.sig +0 -0
- metadata +66 -46
- metadata.gz.sig +0 -0
- data/lib/json_p3/environment.rb +0 -111
- data/lib/json_p3/filter.rb +0 -459
- data/lib/json_p3/function.rb +0 -10
- data/lib/json_p3/function_extensions/count.rb +0 -15
- data/lib/json_p3/function_extensions/length.rb +0 -17
- data/lib/json_p3/function_extensions/match.rb +0 -62
- data/lib/json_p3/function_extensions/pattern.rb +0 -39
- data/lib/json_p3/function_extensions/search.rb +0 -44
- data/lib/json_p3/function_extensions/value.rb +0 -15
- data/lib/json_p3/lexer.rb +0 -419
- data/lib/json_p3/node.rb +0 -44
- data/lib/json_p3/parser.rb +0 -553
- data/lib/json_p3/path.rb +0 -72
- data/lib/json_p3/segment.rb +0 -158
- data/lib/json_p3/selector.rb +0 -306
- data/lib/json_p3/serialize.rb +0 -13
- data/lib/json_p3/token.rb +0 -36
- data/lib/json_p3/unescape.rb +0 -112
data/lib/json_p3/lexer.rb
DELETED
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "set"
|
|
4
|
-
require "strscan"
|
|
5
|
-
require_relative "errors"
|
|
6
|
-
require_relative "token"
|
|
7
|
-
|
|
8
|
-
module JSONP3 # rubocop:disable Style/Documentation
|
|
9
|
-
# Return an array of tokens for the JSONPath expression _query_.
|
|
10
|
-
#
|
|
11
|
-
# @param query [String] the JSONPath expression to tokenize.
|
|
12
|
-
# @return [Array<Token>]
|
|
13
|
-
def self.tokenize(query)
|
|
14
|
-
lexer = Lexer.new(query)
|
|
15
|
-
lexer.run
|
|
16
|
-
tokens = lexer.tokens
|
|
17
|
-
|
|
18
|
-
if !tokens.empty? && tokens.last.type == :token_error
|
|
19
|
-
raise JSONPathSyntaxError.new(tokens.last.message || raise,
|
|
20
|
-
tokens.last)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
tokens
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# JSONPath query expression lexical scanner.
|
|
27
|
-
#
|
|
28
|
-
# @see tokenize
|
|
29
|
-
class Lexer
|
|
30
|
-
RE_INT = /-?[0-9]+/
|
|
31
|
-
RE_NAME = /[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*/
|
|
32
|
-
RE_WHITESPACE = /[ \n\r\t]+/
|
|
33
|
-
S_ESCAPES = Set["b", "f", "n", "r", "t", "u", "/", "\\"].freeze
|
|
34
|
-
|
|
35
|
-
# @dynamic tokens
|
|
36
|
-
attr_reader :tokens
|
|
37
|
-
|
|
38
|
-
def initialize(query)
|
|
39
|
-
@filter_depth = 0
|
|
40
|
-
@paren_stack = []
|
|
41
|
-
@tokens = []
|
|
42
|
-
@start = 0
|
|
43
|
-
@query = query.freeze
|
|
44
|
-
@scanner = StringScanner.new(query)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def run
|
|
48
|
-
state = :lex_root
|
|
49
|
-
state = send(state) until state.nil?
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
protected
|
|
53
|
-
|
|
54
|
-
# Generate a new token with the given type.
|
|
55
|
-
# @param token_type [Symbol] one of the constants defined on the _Token_ class.
|
|
56
|
-
# @param value [String | nil] a the token's value, if it is known, otherwise the
|
|
57
|
-
# value will be sliced from @query. This is a performance optimization.
|
|
58
|
-
def emit(token_type, value = nil)
|
|
59
|
-
@tokens << Token.new(token_type, value || @query[@start, @scanner.charpos - @start], @start, @query)
|
|
60
|
-
@start = @scanner.charpos
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def next
|
|
64
|
-
@scanner.get_byte || ""
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def ignore
|
|
68
|
-
@start = @scanner.charpos
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def backup
|
|
72
|
-
@scanner.pos -= 1
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def peek
|
|
76
|
-
# Assumes we're peeking single byte characters.
|
|
77
|
-
@scanner.peek(1)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Advance the lexer if _pattern_ matches from the current position.
|
|
81
|
-
def accept?(pattern)
|
|
82
|
-
!@scanner.scan(pattern).nil?
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Accept a run of digits, possibly preceded by a negative sign.
|
|
86
|
-
# Does not handle exponents.
|
|
87
|
-
def accept_int?
|
|
88
|
-
!@scanner.scan(RE_INT).nil?
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def ignore_whitespace?
|
|
92
|
-
if @scanner.scan(RE_WHITESPACE).nil?
|
|
93
|
-
false
|
|
94
|
-
else
|
|
95
|
-
ignore
|
|
96
|
-
true
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def error(message)
|
|
101
|
-
@tokens << Token.new(
|
|
102
|
-
:token_error, @query[@start, @scanner.charpos - @start] || "", @start, @query, message: message
|
|
103
|
-
)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def lex_root
|
|
107
|
-
c = self.next
|
|
108
|
-
|
|
109
|
-
unless c == "$"
|
|
110
|
-
error "expected '$', found '#{c}'"
|
|
111
|
-
return nil
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
emit(:token_root, "$")
|
|
115
|
-
:lex_segment
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def lex_segment
|
|
119
|
-
if accept?(RE_WHITESPACE) && peek.empty?
|
|
120
|
-
error "unexpected trailing whitespace"
|
|
121
|
-
return nil
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
ignore
|
|
125
|
-
c = self.next
|
|
126
|
-
|
|
127
|
-
case c
|
|
128
|
-
when ""
|
|
129
|
-
emit(:token_eoi, "")
|
|
130
|
-
nil
|
|
131
|
-
when "."
|
|
132
|
-
return :lex_shorthand_selector unless peek == "."
|
|
133
|
-
|
|
134
|
-
self.next
|
|
135
|
-
emit(:token_double_dot, "..")
|
|
136
|
-
:lex_descendant_segment
|
|
137
|
-
when "["
|
|
138
|
-
emit(:token_lbracket, "[")
|
|
139
|
-
:lex_inside_bracketed_segment
|
|
140
|
-
else
|
|
141
|
-
if @filter_depth.positive?
|
|
142
|
-
backup
|
|
143
|
-
:lex_inside_filter
|
|
144
|
-
else
|
|
145
|
-
error "expected '.', '..' or a bracketed selection, found '#{c}'"
|
|
146
|
-
nil
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def lex_descendant_segment
|
|
152
|
-
case self.next
|
|
153
|
-
when ""
|
|
154
|
-
error "bald descendant segment"
|
|
155
|
-
nil
|
|
156
|
-
when "*"
|
|
157
|
-
emit(:token_wild, "*")
|
|
158
|
-
:lex_segment
|
|
159
|
-
when "["
|
|
160
|
-
emit(:token_lbracket, "[")
|
|
161
|
-
:lex_inside_bracketed_segment
|
|
162
|
-
else
|
|
163
|
-
backup
|
|
164
|
-
if accept?(RE_NAME)
|
|
165
|
-
emit(:token_name)
|
|
166
|
-
:lex_segment
|
|
167
|
-
else
|
|
168
|
-
c = self.next
|
|
169
|
-
error "unexpected descendant selection token '#{c}'"
|
|
170
|
-
nil
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def lex_shorthand_selector
|
|
176
|
-
if peek == ""
|
|
177
|
-
error "unexpected trailing dot"
|
|
178
|
-
return nil
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
ignore # ignore dot
|
|
182
|
-
|
|
183
|
-
if accept?(RE_WHITESPACE)
|
|
184
|
-
error "unexpected whitespace after dot"
|
|
185
|
-
return nil
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
if peek == "*"
|
|
189
|
-
self.next
|
|
190
|
-
emit(:token_wild, "*")
|
|
191
|
-
return :lex_segment
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
if accept?(RE_NAME)
|
|
195
|
-
emit(:token_name)
|
|
196
|
-
return :lex_segment
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
c = self.next
|
|
200
|
-
error "unexpected shorthand selector '#{c}'"
|
|
201
|
-
nil
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def lex_inside_bracketed_segment
|
|
205
|
-
loop do # rubocop:disable Metrics/BlockLength
|
|
206
|
-
ignore_whitespace?
|
|
207
|
-
c = self.next
|
|
208
|
-
|
|
209
|
-
case c
|
|
210
|
-
when "]"
|
|
211
|
-
emit(:token_rbracket, "]")
|
|
212
|
-
return :lex_segment
|
|
213
|
-
when ""
|
|
214
|
-
error "unclosed bracketed selection"
|
|
215
|
-
return nil
|
|
216
|
-
when "*"
|
|
217
|
-
emit(:token_wild, "*")
|
|
218
|
-
when "?"
|
|
219
|
-
emit(:token_filter, "?")
|
|
220
|
-
@filter_depth += 1
|
|
221
|
-
return :lex_inside_filter
|
|
222
|
-
when ","
|
|
223
|
-
emit(:token_comma, ",")
|
|
224
|
-
when ":"
|
|
225
|
-
emit(:token_colon, ":")
|
|
226
|
-
when "'"
|
|
227
|
-
return :lex_single_quoted_string_inside_bracketed_segment
|
|
228
|
-
when '"'
|
|
229
|
-
return :lex_double_quoted_string_inside_bracketed_segment
|
|
230
|
-
else
|
|
231
|
-
backup
|
|
232
|
-
if accept_int?
|
|
233
|
-
# Index selector or part of a slice selector.
|
|
234
|
-
emit(:token_index)
|
|
235
|
-
else
|
|
236
|
-
error "unexpected token '#{c}' in bracketed selection"
|
|
237
|
-
return nil
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def lex_inside_filter # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
244
|
-
loop do # rubocop:disable Metrics/BlockLength
|
|
245
|
-
ignore_whitespace?
|
|
246
|
-
c = self.next
|
|
247
|
-
|
|
248
|
-
case c
|
|
249
|
-
when ""
|
|
250
|
-
error "unclosed bracketed selection"
|
|
251
|
-
return nil
|
|
252
|
-
when "]"
|
|
253
|
-
@filter_depth -= 1
|
|
254
|
-
if @paren_stack.length == 1
|
|
255
|
-
error "unbalanced parentheses"
|
|
256
|
-
return nil
|
|
257
|
-
end
|
|
258
|
-
backup
|
|
259
|
-
return :lex_inside_bracketed_segment
|
|
260
|
-
when ","
|
|
261
|
-
emit(:token_comma, ",")
|
|
262
|
-
# If we have unbalanced parens, we are inside a function call and a
|
|
263
|
-
# comma separates arguments. Otherwise a comma separates selectors.
|
|
264
|
-
next if @paren_stack.length.positive?
|
|
265
|
-
|
|
266
|
-
@filter_depth -= 1
|
|
267
|
-
return :lex_inside_bracketed_segment
|
|
268
|
-
when "'"
|
|
269
|
-
return :lex_single_quoted_string_inside_filter_expression
|
|
270
|
-
when '"'
|
|
271
|
-
return :lex_double_quoted_string_inside_filter_expression
|
|
272
|
-
when "("
|
|
273
|
-
emit(:token_lparen, "(")
|
|
274
|
-
# Are we in a function call? If so, a function argument contains parens.
|
|
275
|
-
@paren_stack[-1] += 1 if @paren_stack.length.positive?
|
|
276
|
-
when ")"
|
|
277
|
-
emit(:token_rparen, ")")
|
|
278
|
-
# Are we closing a function call or a parenthesized expression?
|
|
279
|
-
if @paren_stack.length.positive?
|
|
280
|
-
if @paren_stack[-1] == 1
|
|
281
|
-
@paren_stack.pop
|
|
282
|
-
else
|
|
283
|
-
@paren_stack[-1] -= 1
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
when "$"
|
|
287
|
-
emit(:token_root, "$")
|
|
288
|
-
return :lex_segment
|
|
289
|
-
when "@"
|
|
290
|
-
emit(:token_current, "@")
|
|
291
|
-
return :lex_segment
|
|
292
|
-
when "."
|
|
293
|
-
backup
|
|
294
|
-
return :lex_segment
|
|
295
|
-
when "!"
|
|
296
|
-
if peek == "="
|
|
297
|
-
self.next
|
|
298
|
-
emit(:token_ne, "!=")
|
|
299
|
-
else
|
|
300
|
-
emit(:token_not, "!")
|
|
301
|
-
end
|
|
302
|
-
when "="
|
|
303
|
-
if peek == "="
|
|
304
|
-
self.next
|
|
305
|
-
emit(:token_eq, "==")
|
|
306
|
-
else
|
|
307
|
-
backup
|
|
308
|
-
error "found '=', did you mean '==', '!=', '<=' or '>='?"
|
|
309
|
-
return nil
|
|
310
|
-
end
|
|
311
|
-
when "<"
|
|
312
|
-
if peek == "="
|
|
313
|
-
self.next
|
|
314
|
-
emit(:token_le, "<=")
|
|
315
|
-
else
|
|
316
|
-
emit(:token_lt, "<")
|
|
317
|
-
end
|
|
318
|
-
when ">"
|
|
319
|
-
if peek == "="
|
|
320
|
-
self.next
|
|
321
|
-
emit(:token_ge, ">=")
|
|
322
|
-
else
|
|
323
|
-
emit(:token_gt, ">")
|
|
324
|
-
end
|
|
325
|
-
else
|
|
326
|
-
backup
|
|
327
|
-
if accept_int?
|
|
328
|
-
if peek == "."
|
|
329
|
-
# A float
|
|
330
|
-
self.next
|
|
331
|
-
unless accept_int? # rubocop:disable Metrics/BlockNesting
|
|
332
|
-
error "a fractional digit is required after a decimal point"
|
|
333
|
-
return nil
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
accept?(/[eE][+-]?[0-9]+/)
|
|
337
|
-
emit :token_float
|
|
338
|
-
# An int, or float if exponent is negative
|
|
339
|
-
elsif accept?(/[eE]-[0-9]+/)
|
|
340
|
-
emit :token_float
|
|
341
|
-
else
|
|
342
|
-
accept?(/[eE][+-]?[0-9]+/)
|
|
343
|
-
emit :token_int
|
|
344
|
-
end
|
|
345
|
-
elsif accept?("&&")
|
|
346
|
-
emit(:token_and, "&&")
|
|
347
|
-
elsif accept?("||")
|
|
348
|
-
emit(:token_or, "||")
|
|
349
|
-
elsif accept?("true")
|
|
350
|
-
emit(:token_true, "true")
|
|
351
|
-
elsif accept?("false")
|
|
352
|
-
emit(:token_false, "false")
|
|
353
|
-
elsif accept?("null")
|
|
354
|
-
emit(:token_null, "null")
|
|
355
|
-
elsif accept?(/[a-z][a-z_0-9]*/)
|
|
356
|
-
unless peek == "("
|
|
357
|
-
error "unexpected filter selector token"
|
|
358
|
-
return nil
|
|
359
|
-
end
|
|
360
|
-
# Function name
|
|
361
|
-
# Keep track of parentheses for this function call.
|
|
362
|
-
@paren_stack << 1
|
|
363
|
-
emit :token_function
|
|
364
|
-
self.next
|
|
365
|
-
ignore # move past LPAREN
|
|
366
|
-
else
|
|
367
|
-
error "unexpected filter selector token '#{c}'"
|
|
368
|
-
return nil
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
class << self
|
|
375
|
-
def lex_string_factory(quote, state, token)
|
|
376
|
-
proc {
|
|
377
|
-
# @type self: Lexer
|
|
378
|
-
ignore # move past opening quote
|
|
379
|
-
|
|
380
|
-
loop do
|
|
381
|
-
c = self.next
|
|
382
|
-
|
|
383
|
-
case c
|
|
384
|
-
when ""
|
|
385
|
-
error "unclosed string starting at index #{@start}"
|
|
386
|
-
return nil
|
|
387
|
-
when "\\"
|
|
388
|
-
peeked = peek
|
|
389
|
-
if S_ESCAPES.member?(peeked) || peeked == quote
|
|
390
|
-
self.next
|
|
391
|
-
else
|
|
392
|
-
error "invalid escape"
|
|
393
|
-
return nil
|
|
394
|
-
end
|
|
395
|
-
when quote
|
|
396
|
-
backup
|
|
397
|
-
emit(token)
|
|
398
|
-
self.next
|
|
399
|
-
ignore # move past closing quote
|
|
400
|
-
return state
|
|
401
|
-
end
|
|
402
|
-
end
|
|
403
|
-
}
|
|
404
|
-
end
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
define_method(:lex_double_quoted_string_inside_bracketed_segment,
|
|
408
|
-
lex_string_factory('"', :lex_inside_bracketed_segment, :token_double_quote_string))
|
|
409
|
-
|
|
410
|
-
define_method(:lex_single_quoted_string_inside_bracketed_segment,
|
|
411
|
-
lex_string_factory("'", :lex_inside_bracketed_segment, :token_single_quote_string))
|
|
412
|
-
|
|
413
|
-
define_method(:lex_double_quoted_string_inside_filter_expression,
|
|
414
|
-
lex_string_factory('"', :lex_inside_filter, :token_double_quote_string))
|
|
415
|
-
|
|
416
|
-
define_method(:lex_single_quoted_string_inside_filter_expression,
|
|
417
|
-
lex_string_factory("'", :lex_inside_filter, :token_single_quote_string))
|
|
418
|
-
end
|
|
419
|
-
end
|
data/lib/json_p3/node.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "serialize"
|
|
4
|
-
|
|
5
|
-
module JSONP3
|
|
6
|
-
# A JSON-like value and its location.
|
|
7
|
-
class JSONPathNode
|
|
8
|
-
# @dynamic value, location, root
|
|
9
|
-
attr_reader :value, :location, :root
|
|
10
|
-
|
|
11
|
-
# @param value [JSON-like] the value at this node.
|
|
12
|
-
# @param location [Array<String | Integer | Array<String | Integer>>] the sequence of
|
|
13
|
-
# names and/or indices leading to _value_ in _root_.
|
|
14
|
-
# @param root [JSON-like] the root value containing _value_ at _location_.
|
|
15
|
-
def initialize(value, location, root)
|
|
16
|
-
@value = value
|
|
17
|
-
@location = location
|
|
18
|
-
@root = root
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Return the normalized path to this node.
|
|
22
|
-
# @return [String] the normalized path.
|
|
23
|
-
def path
|
|
24
|
-
segments = @location.flatten.map { |i| i.is_a?(String) ? "[#{JSONP3.canonical_string(i)}]" : "[#{i}]" }
|
|
25
|
-
"$#{segments.join}"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Return a new node that is a child of this node.
|
|
29
|
-
# @param value the JSON-like value at the new node.
|
|
30
|
-
# @param key [Integer, String] the array index or hash key associated with _value_.
|
|
31
|
-
def new_child(value, key)
|
|
32
|
-
JSONPathNode.new(value, [@location, key], @root)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def to_s
|
|
36
|
-
"JSONPathNode(#{value} at #{path})"
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# An array of JSONPathNode instances. We use this internally to differentiate
|
|
41
|
-
# arrays of Nodes and arrays of data values, which is required when calling
|
|
42
|
-
# filter functions expecting nodes as arguments. It is just an array though.
|
|
43
|
-
class JSONPathNodeList < Array; end
|
|
44
|
-
end
|