json_path_rfc9535 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +455 -0
- data/CHANGELOG.md +13 -0
- data/README.md +126 -0
- data/lib/json_path/doc.rb +27 -0
- data/lib/json_path/error.rb +4 -0
- data/lib/json_path/multiple_values_returned_by_singular_query.rb +6 -0
- data/lib/json_path/node_list.rb +27 -0
- data/lib/json_path/nodes/array.rb +19 -0
- data/lib/json_path/nodes/base.rb +13 -0
- data/lib/json_path/nodes/false.rb +9 -0
- data/lib/json_path/nodes/null.rb +9 -0
- data/lib/json_path/nodes/number.rb +12 -0
- data/lib/json_path/nodes/object.rb +19 -0
- data/lib/json_path/nodes/string.rb +12 -0
- data/lib/json_path/nodes/true.rb +9 -0
- data/lib/json_path/nodes.rb +27 -0
- data/lib/json_path/parser/core.rb +75 -0
- data/lib/json_path/parser/raw_parser.rb +73 -0
- data/lib/json_path/parser/transformer.rb +219 -0
- data/lib/json_path/parser.rb +16 -0
- data/lib/json_path/path.rb +22 -0
- data/lib/json_path/unrecognized_node.rb +4 -0
- data/lib/json_path.rb +7 -0
- data/lib/json_path_rfc9535/version.rb +3 -0
- data/lib/json_path_rfc9535.rb +2 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 341631a563a6872bcf0f04f58fdb547a5a17500f3f7fae04ba29ada044c17dbd
|
4
|
+
data.tar.gz: '0669414a8c33cd4ddfd67f2e330d72783ec862ba1a26573053a5bbf372a6b26e'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c355da298d64c50f35f59a76bbff44b2511de1dafbf65181a078db74909433396dd86f803ecd1b821ce0bf4327264786298d7189e5c7ee37777bf35e53dacc8e
|
7
|
+
data.tar.gz: f8e4f5e8a131adb0b1d88b9a820218687db2a146fea1a4ef91e33adb461b1d299285895c5fde88554f6ac14d87b7e86235ed97734253f397e06e741a5f270b13
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,455 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
Exclude:
|
4
|
+
- 'bin/**'
|
5
|
+
|
6
|
+
Gemspec/DeprecatedAttributeAssignment:
|
7
|
+
Enabled: true
|
8
|
+
|
9
|
+
Gemspec/DevelopmentDependencies:
|
10
|
+
Enabled: true
|
11
|
+
|
12
|
+
Gemspec/RequireMFA:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Layout/AccessModifierIndentation:
|
16
|
+
EnforcedStyle: outdent
|
17
|
+
|
18
|
+
Layout/BeginEndAlignment:
|
19
|
+
EnforcedStyleAlignWith: begin
|
20
|
+
|
21
|
+
Layout/BlockAlignment:
|
22
|
+
EnforcedStyleAlignWith: start_of_block
|
23
|
+
|
24
|
+
Layout/CommentIndentation:
|
25
|
+
AllowForAlignment: true
|
26
|
+
|
27
|
+
Layout/EmptyLineAfterMultilineCondition:
|
28
|
+
Enabled: true
|
29
|
+
|
30
|
+
Layout/EndOfLine:
|
31
|
+
EnforcedStyle: lf
|
32
|
+
|
33
|
+
Layout/ExtraSpacing:
|
34
|
+
AllowForAlignment: true
|
35
|
+
AllowBeforeTrailingComments: true
|
36
|
+
|
37
|
+
Layout/HashAlignment:
|
38
|
+
EnforcedHashRocketStyle: table
|
39
|
+
EnforcedColonStyle: table
|
40
|
+
EnforcedLastArgumentHashStyle: ignore_implicit
|
41
|
+
|
42
|
+
Layout/LineContinuationLeadingSpace:
|
43
|
+
Enabled: true
|
44
|
+
|
45
|
+
Layout/LineContinuationSpacing:
|
46
|
+
Enabled: true
|
47
|
+
|
48
|
+
Layout/LineEndStringConcatenationIndentation:
|
49
|
+
Enabled: true
|
50
|
+
|
51
|
+
Layout/MultilineArrayLineBreaks:
|
52
|
+
Enabled: true
|
53
|
+
|
54
|
+
Layout/MultilineAssignmentLayout:
|
55
|
+
Enabled: true
|
56
|
+
EnforcedStyle: same_line
|
57
|
+
|
58
|
+
Layout/MultilineHashKeyLineBreaks:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
Layout/MultilineMethodArgumentLineBreaks:
|
62
|
+
Enabled: true
|
63
|
+
|
64
|
+
Layout/MultilineMethodCallIndentation:
|
65
|
+
EnforcedStyle: indented_relative_to_receiver
|
66
|
+
|
67
|
+
Layout/SingleLineBlockChain:
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
71
|
+
EnforcedStyle: no_space
|
72
|
+
|
73
|
+
Layout/SpaceAroundOperators:
|
74
|
+
EnforcedStyleForExponentOperator: space
|
75
|
+
|
76
|
+
Layout/SpaceBeforeBrackets:
|
77
|
+
Enabled: true
|
78
|
+
|
79
|
+
Layout/SpaceInsideHashLiteralBraces:
|
80
|
+
EnforcedStyle: no_space
|
81
|
+
|
82
|
+
Layout/TrailingWhitespace:
|
83
|
+
AllowInHeredoc: true
|
84
|
+
|
85
|
+
Lint/AmbiguousAssignment:
|
86
|
+
Enabled: true
|
87
|
+
|
88
|
+
Lint/AmbiguousOperatorPrecedence:
|
89
|
+
Enabled: true
|
90
|
+
|
91
|
+
Lint/AmbiguousRange:
|
92
|
+
Enabled: true
|
93
|
+
RequireParenthesesForMethodChains: true
|
94
|
+
|
95
|
+
Lint/AssignmentInCondition:
|
96
|
+
AllowSafeAssignment: false
|
97
|
+
|
98
|
+
Lint/ConstantOverwrittenInRescue:
|
99
|
+
Enabled: true
|
100
|
+
|
101
|
+
Lint/DeprecatedConstants:
|
102
|
+
Enabled: true
|
103
|
+
|
104
|
+
Lint/DuplicateBranch:
|
105
|
+
Enabled: true
|
106
|
+
IgnoreLiteralBranches: true
|
107
|
+
IgnoreConstantBranches: true
|
108
|
+
|
109
|
+
Lint/DuplicateMagicComment:
|
110
|
+
Enabled: true
|
111
|
+
|
112
|
+
Lint/DuplicateRegexpCharacterClassElement:
|
113
|
+
Enabled: true
|
114
|
+
|
115
|
+
Lint/EmptyBlock:
|
116
|
+
Enabled: true
|
117
|
+
|
118
|
+
Lint/EmptyClass:
|
119
|
+
Enabled: true
|
120
|
+
AllowComments: true
|
121
|
+
|
122
|
+
Lint/EmptyInPattern:
|
123
|
+
Enabled: true
|
124
|
+
|
125
|
+
Lint/HeredocMethodCallPosition:
|
126
|
+
Enabled: true
|
127
|
+
|
128
|
+
Lint/IncompatibleIoSelectWithFiberScheduler:
|
129
|
+
Enabled: false
|
130
|
+
|
131
|
+
Lint/LambdaWithoutLiteralBlock:
|
132
|
+
Enabled: true
|
133
|
+
|
134
|
+
Lint/NoReturnInBeginEndBlocks:
|
135
|
+
Enabled: true
|
136
|
+
|
137
|
+
Lint/NonAtomicFileOperation:
|
138
|
+
Enabled: true
|
139
|
+
|
140
|
+
Lint/NumberedParameterAssignment:
|
141
|
+
Enabled: true
|
142
|
+
|
143
|
+
Lint/OrAssignmentToConstant:
|
144
|
+
Enabled: true
|
145
|
+
|
146
|
+
Lint/RedundantDirGlobSort:
|
147
|
+
Enabled: true
|
148
|
+
|
149
|
+
Lint/RedundantSplatExpansion:
|
150
|
+
AllowPercentLiteralArrayArgument: false
|
151
|
+
|
152
|
+
Lint/RefinementImportMethods:
|
153
|
+
Enabled: true
|
154
|
+
|
155
|
+
Lint/RequireRangeParentheses:
|
156
|
+
Enabled: true
|
157
|
+
|
158
|
+
Lint/RequireRelativeSelfPath:
|
159
|
+
Enabled: true
|
160
|
+
|
161
|
+
Lint/SymbolConversion:
|
162
|
+
Enabled: true
|
163
|
+
|
164
|
+
Lint/ToEnumArguments:
|
165
|
+
Enabled: true
|
166
|
+
|
167
|
+
Lint/TripleQuotes:
|
168
|
+
Enabled: true
|
169
|
+
|
170
|
+
Lint/UnexpectedBlockArity:
|
171
|
+
Enabled: true
|
172
|
+
|
173
|
+
Lint/UnmodifiedReduceAccumulator:
|
174
|
+
Enabled: true
|
175
|
+
|
176
|
+
Lint/UnusedBlockArgument:
|
177
|
+
AutoCorrect: false
|
178
|
+
|
179
|
+
Lint/UnusedMethodArgument:
|
180
|
+
AutoCorrect: false
|
181
|
+
|
182
|
+
Lint/UselessRescue:
|
183
|
+
Enabled: true
|
184
|
+
|
185
|
+
Lint/UselessRuby2Keywords:
|
186
|
+
Enabled: true
|
187
|
+
|
188
|
+
Metrics:
|
189
|
+
Enabled: false
|
190
|
+
|
191
|
+
Naming/BlockForwarding:
|
192
|
+
Enabled: true
|
193
|
+
|
194
|
+
Naming/InclusiveLanguage:
|
195
|
+
Enabled: false
|
196
|
+
|
197
|
+
Security/CompoundHash:
|
198
|
+
Enabled: true
|
199
|
+
|
200
|
+
Security/Eval:
|
201
|
+
Enabled: false
|
202
|
+
|
203
|
+
Security/IoMethods:
|
204
|
+
Enabled: true
|
205
|
+
|
206
|
+
Security/YAMLLoad:
|
207
|
+
Enabled: false
|
208
|
+
|
209
|
+
Style/AccessorGrouping:
|
210
|
+
EnforcedStyle: separated
|
211
|
+
|
212
|
+
Style/ArgumentsForwarding:
|
213
|
+
Enabled: false
|
214
|
+
|
215
|
+
Style/ArrayIntersect:
|
216
|
+
Enabled: true
|
217
|
+
|
218
|
+
Style/AutoResourceCleanup:
|
219
|
+
Enabled: true
|
220
|
+
|
221
|
+
Style/CollectionCompact:
|
222
|
+
Enabled: true
|
223
|
+
|
224
|
+
Style/CollectionMethods:
|
225
|
+
Enabled: true
|
226
|
+
|
227
|
+
Style/ComparableClamp:
|
228
|
+
Enabled: true
|
229
|
+
|
230
|
+
Style/ConcatArrayLiterals:
|
231
|
+
Enabled: true
|
232
|
+
|
233
|
+
Style/DirEmpty:
|
234
|
+
Enabled: true
|
235
|
+
|
236
|
+
Style/DocumentDynamicEvalDefinition:
|
237
|
+
Enabled: false
|
238
|
+
|
239
|
+
Style/Documentation:
|
240
|
+
Enabled: false
|
241
|
+
|
242
|
+
Style/DocumentationMethod:
|
243
|
+
Enabled: false
|
244
|
+
|
245
|
+
Style/DoubleNegation:
|
246
|
+
EnforcedStyle: forbidden
|
247
|
+
|
248
|
+
Style/EmptyHeredoc:
|
249
|
+
Enabled: true
|
250
|
+
|
251
|
+
Style/EmptyMethod:
|
252
|
+
EnforcedStyle: expanded
|
253
|
+
|
254
|
+
Style/EndlessMethod:
|
255
|
+
Enabled: true
|
256
|
+
EnforcedStyle: disallow
|
257
|
+
|
258
|
+
Style/EnvHome:
|
259
|
+
Enabled: true
|
260
|
+
|
261
|
+
Style/FetchEnvVar:
|
262
|
+
Enabled: false
|
263
|
+
|
264
|
+
Style/FileEmpty:
|
265
|
+
Enabled: true
|
266
|
+
|
267
|
+
Style/FileRead:
|
268
|
+
Enabled: true
|
269
|
+
|
270
|
+
Style/FileWrite:
|
271
|
+
Enabled: true
|
272
|
+
|
273
|
+
Style/FormatString:
|
274
|
+
EnforcedStyle: percent
|
275
|
+
|
276
|
+
Style/FrozenStringLiteralComment:
|
277
|
+
Enabled: false
|
278
|
+
|
279
|
+
Style/HashConversion:
|
280
|
+
Enabled: true
|
281
|
+
|
282
|
+
Style/HashExcept:
|
283
|
+
Enabled: true
|
284
|
+
|
285
|
+
Style/HashSyntax:
|
286
|
+
EnforcedShorthandSyntax: never
|
287
|
+
|
288
|
+
Style/IfWithBooleanLiteralBranches:
|
289
|
+
Enabled: true
|
290
|
+
|
291
|
+
Style/ImplicitRuntimeError:
|
292
|
+
Enabled: true
|
293
|
+
|
294
|
+
Style/InPatternThen:
|
295
|
+
Enabled: true
|
296
|
+
|
297
|
+
Style/IpAddresses:
|
298
|
+
Enabled: true
|
299
|
+
|
300
|
+
Style/MagicCommentFormat:
|
301
|
+
Enabled: true
|
302
|
+
|
303
|
+
Style/MapCompactWithConditionalBlock:
|
304
|
+
Enabled: true
|
305
|
+
|
306
|
+
Style/MapToHash:
|
307
|
+
Enabled: true
|
308
|
+
|
309
|
+
Style/MapToSet:
|
310
|
+
Enabled: true
|
311
|
+
|
312
|
+
Style/MethodCallWithArgsParentheses:
|
313
|
+
Enabled: true
|
314
|
+
EnforcedStyle: omit_parentheses
|
315
|
+
AllowParenthesesInMultilineCall: true
|
316
|
+
AllowParenthesesInChaining: true
|
317
|
+
AllowParenthesesInCamelCaseMethod: true
|
318
|
+
|
319
|
+
Style/MethodDefParentheses:
|
320
|
+
EnforcedStyle: require_no_parentheses_except_multiline
|
321
|
+
|
322
|
+
Style/MinMaxComparison:
|
323
|
+
Enabled: true
|
324
|
+
|
325
|
+
Style/MultilineBlockChain:
|
326
|
+
Enabled: false
|
327
|
+
|
328
|
+
Style/MultilineInPatternThen:
|
329
|
+
Enabled: true
|
330
|
+
|
331
|
+
Style/NegatedIfElseCondition:
|
332
|
+
Enabled: true
|
333
|
+
|
334
|
+
Style/NestedFileDirname:
|
335
|
+
Enabled: true
|
336
|
+
|
337
|
+
Style/NestedParenthesizedCalls:
|
338
|
+
Enabled: false
|
339
|
+
|
340
|
+
Style/NilLambda:
|
341
|
+
Enabled: true
|
342
|
+
|
343
|
+
Style/NonNilCheck:
|
344
|
+
Enabled: false
|
345
|
+
|
346
|
+
Style/NumberedParameters:
|
347
|
+
Enabled: true
|
348
|
+
EnforcedStyle: disallow
|
349
|
+
|
350
|
+
Style/NumberedParametersLimit:
|
351
|
+
Enabled: true
|
352
|
+
|
353
|
+
Style/ObjectThen:
|
354
|
+
Enabled: true
|
355
|
+
|
356
|
+
Style/OpenStructUse:
|
357
|
+
Enabled: false
|
358
|
+
|
359
|
+
Style/OperatorMethodCall:
|
360
|
+
Enabled: true
|
361
|
+
|
362
|
+
Style/OptionHash:
|
363
|
+
Enabled: true
|
364
|
+
|
365
|
+
Style/QuotedSymbols:
|
366
|
+
Enabled: true
|
367
|
+
|
368
|
+
Style/RedundantArgument:
|
369
|
+
Enabled: false
|
370
|
+
|
371
|
+
Style/RedundantConstantBase:
|
372
|
+
Enabled: false
|
373
|
+
|
374
|
+
Style/RedundantDoubleSplatHashBraces:
|
375
|
+
Enabled: true
|
376
|
+
|
377
|
+
Style/RedundantEach:
|
378
|
+
Enabled: true
|
379
|
+
|
380
|
+
Style/RedundantException:
|
381
|
+
Enabled: false
|
382
|
+
|
383
|
+
Style/RedundantHeredocDelimiterQuotes:
|
384
|
+
Enabled: true
|
385
|
+
|
386
|
+
Style/RedundantInitialize:
|
387
|
+
Enabled: true
|
388
|
+
|
389
|
+
Style/RedundantParentheses:
|
390
|
+
Enabled: false
|
391
|
+
|
392
|
+
Style/RedundantSelfAssignmentBranch:
|
393
|
+
Enabled: true
|
394
|
+
|
395
|
+
Style/RedundantStringEscape:
|
396
|
+
Enabled: true
|
397
|
+
|
398
|
+
Style/RegexpLiteral:
|
399
|
+
EnforcedStyle: percent_r
|
400
|
+
|
401
|
+
Style/ReturnNil:
|
402
|
+
Enabled: true
|
403
|
+
|
404
|
+
Style/SelectByRegexp:
|
405
|
+
Enabled: true
|
406
|
+
|
407
|
+
Style/SingleLineMethods:
|
408
|
+
AllowIfMethodIsEmpty: false
|
409
|
+
|
410
|
+
Style/StaticClass:
|
411
|
+
Enabled: true
|
412
|
+
|
413
|
+
Style/StringChars:
|
414
|
+
Enabled: true
|
415
|
+
|
416
|
+
Style/StringHashKeys:
|
417
|
+
Enabled: false
|
418
|
+
|
419
|
+
Style/SwapValues:
|
420
|
+
Enabled: true
|
421
|
+
|
422
|
+
Style/SymbolArray:
|
423
|
+
EnforcedStyle: brackets
|
424
|
+
|
425
|
+
Style/TernaryParentheses:
|
426
|
+
EnforcedStyle: require_parentheses_when_complex
|
427
|
+
AllowSafeAssignment: false
|
428
|
+
|
429
|
+
Style/TopLevelMethodDefinition:
|
430
|
+
Enabled: true
|
431
|
+
|
432
|
+
Style/TrailingCommaInArguments:
|
433
|
+
Enabled: true
|
434
|
+
EnforcedStyleForMultiline: no_comma
|
435
|
+
|
436
|
+
Style/TrailingCommaInArrayLiteral:
|
437
|
+
Enabled: true
|
438
|
+
EnforcedStyleForMultiline: no_comma
|
439
|
+
|
440
|
+
Style/TrailingCommaInBlockArgs:
|
441
|
+
Enabled: true
|
442
|
+
|
443
|
+
Style/TrailingCommaInHashLiteral:
|
444
|
+
Enabled: true
|
445
|
+
EnforcedStyleForMultiline: no_comma
|
446
|
+
|
447
|
+
Style/UnlessLogicalOperators:
|
448
|
+
EnforcedStyle: forbid_logical_operators
|
449
|
+
|
450
|
+
Style/WordArray:
|
451
|
+
EnforcedStyle: brackets
|
452
|
+
|
453
|
+
Style/YodaCondition:
|
454
|
+
Enabled: true
|
455
|
+
EnforcedStyle: forbid_for_all_comparison_operators
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
<!--[//]: # (
|
4
|
+
## <Release number> <Date YYYY-MM-DD>
|
5
|
+
### Breaking changes
|
6
|
+
### Deprecations
|
7
|
+
### New features
|
8
|
+
### Bug fixes
|
9
|
+
)-->
|
10
|
+
|
11
|
+
## 1.0.0 2024-09-09
|
12
|
+
|
13
|
+
First public release. Refer to [README.md](README.md) for the full documentation.
|
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Json Path RFC 9535
|
2
|
+
|
3
|
+
A Ruby implementation of RFC 9535.
|
4
|
+
|
5
|
+
Like XPath is a query language for XML, JsonPath is a query language for JSON. This gem aims to be an implementation of RFC 9535. Unlike tha original JsonPath description (http://goessner.net/articles/JsonPath/), RFC 9535 is strictly normative, which ideally should leave open fewer doors for inconsistencies.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'json_path_rfc9535', '~> 1.0'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or you can install the gem on its own:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
gem install json_path_rfc9535
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Parse the Json into a `JsonPath::Doc` instance...
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
doc = JsonPath::Doc(<<~JSON)
|
33
|
+
{
|
34
|
+
"store": {
|
35
|
+
"book": [
|
36
|
+
{
|
37
|
+
"category": "reference",
|
38
|
+
"author": "Nigel Rees",
|
39
|
+
"title": "Sayings of the Century",
|
40
|
+
"price": 8.95
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"category": "fiction",
|
44
|
+
"author": "Evelyn Waugh",
|
45
|
+
"title": "Sword of Honour",
|
46
|
+
"price": 12.99
|
47
|
+
},
|
48
|
+
{
|
49
|
+
"category": "fiction",
|
50
|
+
"author": "Herman Melville",
|
51
|
+
"title": "Moby Dick",
|
52
|
+
"isbn": "0-553-21311-3",
|
53
|
+
"price": 8.99
|
54
|
+
},
|
55
|
+
{
|
56
|
+
"category": "fiction",
|
57
|
+
"author": "J. R. R. Tolkien",
|
58
|
+
"title": "The Lord of the Rings",
|
59
|
+
"isbn": "0-395-19395-8",
|
60
|
+
"price": 22.99
|
61
|
+
}
|
62
|
+
],
|
63
|
+
"bicycle": {
|
64
|
+
"color": "red",
|
65
|
+
"price": 399
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
JSON
|
70
|
+
```
|
71
|
+
|
72
|
+
... and then query it.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
doc.query('$.store.bicycle.color')
|
76
|
+
```
|
77
|
+
|
78
|
+
The returned object has methods to retrieve the values or the paths of all the retrieved nodes:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
doc.query('$.store.book.*.category').values
|
82
|
+
# => ["reference", "fiction", "fiction", "fiction"]
|
83
|
+
doc.query('$.store.book.*.category').paths
|
84
|
+
# => ["$['store']['book'][0]['category']", "$['store']['book'][1]['category']", "$['store']['book'][2]['category']", "$['store']['book'][3]['category']"]
|
85
|
+
```
|
86
|
+
|
87
|
+
You can also query it further:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
results = doc.query('$.store.book[?(@.price > 10)]')
|
91
|
+
results.paths
|
92
|
+
# => ["$['store']['book'][1]", "$['store']['book'][3]"]
|
93
|
+
results.query('$.author').values
|
94
|
+
# => ["Evelyn Waugh", "J. R. R. Tolkien"]
|
95
|
+
```
|
96
|
+
|
97
|
+
This gem implements most of RFC 9535, with the exception of [function extensions](https://datatracker.ietf.org/doc/html/rfc9535#name-function-extensions) and the related [type system](https://datatracker.ietf.org/doc/html/rfc9535#name-type-system-for-function-ex). It also relies on the underlying Ruby interpreter for string evaluation, meaning that characters don't need to be double-escaped.
|
98
|
+
|
99
|
+
## Plans for future development
|
100
|
+
|
101
|
+
- Function extensions
|
102
|
+
- Function extensions type system
|
103
|
+
|
104
|
+
## Version numbers
|
105
|
+
|
106
|
+
Json Path RFC 9535 loosely follows [Semantic Versioning](https://semver.org/), with a hard guarantee that breaking changes to the public API will always coincide with an increase to the `MAJOR` number.
|
107
|
+
|
108
|
+
Version numbers are in three parts: `MAJOR.MINOR.PATCH`.
|
109
|
+
|
110
|
+
- Breaking changes to the public API increment the `MAJOR`. There may also be changes that would otherwise increase the `MINOR` or the `PATCH`.
|
111
|
+
- Additions, deprecations, and "big" non breaking changes to the public API increment the `MINOR`. There may also be changes that would otherwise increase the `PATCH`.
|
112
|
+
- Bug fixes and "small" non breaking changes to the public API increment the `PATCH`.
|
113
|
+
|
114
|
+
Notice that any feature deprecated by a minor release can be expected to be removed by the next major release.
|
115
|
+
|
116
|
+
## Changelog
|
117
|
+
|
118
|
+
Full list of changes in [CHANGELOG.md](CHANGELOG.md)
|
119
|
+
|
120
|
+
## Contributing
|
121
|
+
|
122
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/moku-io/json_path_rfc9535.
|
123
|
+
|
124
|
+
## License
|
125
|
+
|
126
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'nodes'
|
3
|
+
require_relative 'path'
|
4
|
+
require_relative 'node_list'
|
5
|
+
|
6
|
+
module JsonPath
|
7
|
+
class Doc
|
8
|
+
attr_reader :root_node
|
9
|
+
|
10
|
+
def initialize json_string, parse_json: json_string.is_a?(String)
|
11
|
+
json = (parse_json ? JSON.parse(json_string) : json_string)
|
12
|
+
@root_node = Nodes.parse '$', json
|
13
|
+
end
|
14
|
+
|
15
|
+
def query json_path
|
16
|
+
json_path = Path.new json_path
|
17
|
+
|
18
|
+
json_path
|
19
|
+
.apply(root_node)
|
20
|
+
.then { NodeList.new _1 }
|
21
|
+
end
|
22
|
+
|
23
|
+
def value
|
24
|
+
root_node.value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'path'
|
2
|
+
|
3
|
+
module JsonPath
|
4
|
+
class NodeList
|
5
|
+
attr_reader :nodes
|
6
|
+
|
7
|
+
def initialize nodes
|
8
|
+
@nodes = nodes
|
9
|
+
end
|
10
|
+
|
11
|
+
def query json_path
|
12
|
+
json_path = Path.new json_path
|
13
|
+
|
14
|
+
nodes
|
15
|
+
.flat_map { json_path.apply _1 }
|
16
|
+
.then { self.class.new _1 }
|
17
|
+
end
|
18
|
+
|
19
|
+
def values
|
20
|
+
nodes.map(&:value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def paths
|
24
|
+
nodes.map(&:path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JsonPath
|
2
|
+
module Nodes
|
3
|
+
class Array < Base
|
4
|
+
alias elements children
|
5
|
+
|
6
|
+
def initialize path, values
|
7
|
+
super path
|
8
|
+
@children = values
|
9
|
+
.map.with_index do |value, i|
|
10
|
+
Nodes.parse "#{path}[#{i}]", value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def value
|
15
|
+
elements.map(&:value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JsonPath
|
2
|
+
module Nodes
|
3
|
+
class Object < Base
|
4
|
+
attr_reader :hash
|
5
|
+
|
6
|
+
def initialize path, hash
|
7
|
+
super path
|
8
|
+
@hash = hash.to_h do |key, value|
|
9
|
+
[key, Nodes.parse("#{path}['#{key}']", value)]
|
10
|
+
end
|
11
|
+
@children = self.hash.values
|
12
|
+
end
|
13
|
+
|
14
|
+
def value
|
15
|
+
hash.transform_values(&:value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'nodes/base'
|
2
|
+
Dir["#{__dir__}/nodes/**"].each { |filename| require_relative filename }
|
3
|
+
|
4
|
+
module JsonPath
|
5
|
+
module Nodes
|
6
|
+
def self.parse path, value
|
7
|
+
case value
|
8
|
+
when nil
|
9
|
+
Null.new path
|
10
|
+
when true
|
11
|
+
True.new path
|
12
|
+
when false
|
13
|
+
False.new path
|
14
|
+
when ::String
|
15
|
+
String.new path, value
|
16
|
+
when Numeric
|
17
|
+
Number.new path, value
|
18
|
+
when ::Array
|
19
|
+
Array.new path, value
|
20
|
+
when Hash
|
21
|
+
Object.new path, value
|
22
|
+
else
|
23
|
+
raise UnrecognizedNode, "JSON value expected, #{value.class} found"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module JsonPath
|
4
|
+
module Parser
|
5
|
+
class Core < Parslet::Parser
|
6
|
+
# Never matches
|
7
|
+
rule(:none) { match('').absent? }
|
8
|
+
# Always matches, but doesn't consume any input
|
9
|
+
rule(:empty) { str('') }
|
10
|
+
|
11
|
+
def many parser
|
12
|
+
parser.repeat
|
13
|
+
end
|
14
|
+
|
15
|
+
def some parser
|
16
|
+
parser.repeat 1
|
17
|
+
end
|
18
|
+
|
19
|
+
rule(:digit) { match('[0-9]') }
|
20
|
+
rule(:digits) { digit.repeat(1) }
|
21
|
+
rule(:double_quoted_string_char) { match('[^\\\\"]') | str('\\\\') | str('\\"') }
|
22
|
+
rule(:single_quoted_string_char) { match("[^\\\\']") | str('\\\\') | str("\\'") }
|
23
|
+
rule(:whitespace) { match('[\s]').repeat }
|
24
|
+
|
25
|
+
rule(:int) { str('-').maybe >> digits }
|
26
|
+
rule :num do
|
27
|
+
int >>
|
28
|
+
(str('.') >> digits).maybe >>
|
29
|
+
(str('e') >> (str('-') | str('+')).maybe >> digits).maybe
|
30
|
+
end
|
31
|
+
|
32
|
+
rule(:double_quoted_string) { token(str('"') >> many(double_quoted_string_char).as(:string) >> str('"')) }
|
33
|
+
rule(:single_quoted_string) { token(str("'") >> many(single_quoted_string_char).as(:string) >> str("'")) }
|
34
|
+
|
35
|
+
rule(:true_constant) { symbol('true') }
|
36
|
+
rule(:false_constant) { symbol('false') }
|
37
|
+
rule(:integer) { token(int) }
|
38
|
+
rule(:number) { token(num) }
|
39
|
+
rule(:string) { double_quoted_string | single_quoted_string }
|
40
|
+
|
41
|
+
def symbol string
|
42
|
+
token(str(string))
|
43
|
+
end
|
44
|
+
|
45
|
+
def many_separated parser, separator_parser
|
46
|
+
some_separated(parser, separator_parser).maybe
|
47
|
+
end
|
48
|
+
|
49
|
+
def some_separated parser, separator_parser
|
50
|
+
parser >> many(separator_parser >> parser)
|
51
|
+
end
|
52
|
+
|
53
|
+
def any_unless parser
|
54
|
+
parser.absent? >> any
|
55
|
+
end
|
56
|
+
|
57
|
+
def any_until parser, prefix_name=nil
|
58
|
+
prefix_parser = some(any_unless(parser))
|
59
|
+
prefix_parser = prefix_parser.as(prefix_name) if prefix_name.present?
|
60
|
+
prefix_parser
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Takes a block which needs to be a predicate over strings
|
66
|
+
def predicate parser
|
67
|
+
parser.capture(:input) >> dynamic { |_, c| yield(c.captures[:input].to_s) ? empty : none }
|
68
|
+
end
|
69
|
+
|
70
|
+
def token parser
|
71
|
+
whitespace.maybe >> parser >> whitespace
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'core'
|
2
|
+
|
3
|
+
module JsonPath
|
4
|
+
module Parser
|
5
|
+
class RawParser < Core
|
6
|
+
rule(:jsonpath_query) { root_identifier >> many(segment).as(:segments) }
|
7
|
+
root :jsonpath_query
|
8
|
+
|
9
|
+
rule(:root_identifier) { str('$') }
|
10
|
+
rule(:current_node_identifier) { str('@') }
|
11
|
+
|
12
|
+
# Selectors
|
13
|
+
rule(:selector) { name_selector | wildcard_selector | slice_selector | index_selector | filter_selector }
|
14
|
+
rule(:name_selector) { string.as(:name) }
|
15
|
+
rule(:wildcard_selector) { str('*').as(:wildcard) }
|
16
|
+
rule(:index_selector) { int.as(:index) }
|
17
|
+
rule :slice_selector do
|
18
|
+
integer.maybe.as(:start) >>
|
19
|
+
str(':') >> whitespace >>
|
20
|
+
integer.maybe.as(:end) >>
|
21
|
+
(str(':') >> whitespace >> integer.maybe.as(:step)).maybe
|
22
|
+
end
|
23
|
+
rule(:filter_selector) { str('?') >> whitespace >> logical_expr.as(:filter) }
|
24
|
+
|
25
|
+
# Logical expressions
|
26
|
+
rule(:logical_expr) { logical_or_expr }
|
27
|
+
rule(:logical_or_expr) { some_separated(logical_and_expr.as(:logical_or_operands), symbol('||')) }
|
28
|
+
rule(:logical_and_expr) { some_separated(basic_expr.as(:logical_and_operands), symbol('&&')) }
|
29
|
+
rule(:basic_expr) { paren_expr | comparison_expr | test_expr }
|
30
|
+
rule(:logical_not_op) { symbol('!') }
|
31
|
+
rule :paren_expr do
|
32
|
+
logical_not_op.maybe.as(:negation) >> symbol('(') >> logical_expr.as(:parenthesized_expr) >> symbol(')')
|
33
|
+
end
|
34
|
+
rule(:test_expr) { logical_not_op.maybe.as(:negation) >> (filter_query | function_expr).as(:test_expr) }
|
35
|
+
rule(:filter_query) { (rel_query | jsonpath_query).as(:filter_query) }
|
36
|
+
rule(:rel_query) { current_node_identifier >> many(segment).as(:relative_segments) }
|
37
|
+
rule :comparison_expr do
|
38
|
+
comparable.as(:comparable1) >> comparison_op.as(:comparison_op) >> comparable.as(:comparable2)
|
39
|
+
end
|
40
|
+
rule(:comparison_op) { symbol('==') | symbol('!=') | symbol('<=') | symbol('>=') | symbol('<') | symbol('>') }
|
41
|
+
rule(:comparable) { literal | singular_query | function_expr }
|
42
|
+
rule :literal do
|
43
|
+
number.as(:literal_number) |
|
44
|
+
string.as(:literal_string) |
|
45
|
+
symbol('true').as(:literal_true) |
|
46
|
+
symbol('false').as(:literal_false) |
|
47
|
+
symbol('null').as(:literal_null)
|
48
|
+
end
|
49
|
+
rule(:singular_query) { (rel_singular_query | abs_singular_query).as(:singular_query) }
|
50
|
+
rule :rel_singular_query do
|
51
|
+
current_node_identifier >> many(singular_query_segment).as(:relative_segments)
|
52
|
+
end
|
53
|
+
rule :abs_singular_query do
|
54
|
+
root_identifier >> many(singular_query_segment).as(:segments)
|
55
|
+
end
|
56
|
+
rule(:singular_query_segment) { name_segment | index_segment }
|
57
|
+
rule(:name_segment) { ((str('[') >> name_selector >> str(']')) | (str('.') >> member_name_shorthand)).as(:child) }
|
58
|
+
rule(:index_segment) { (str('[') >> index_selector >> str(']')).as(:child) }
|
59
|
+
rule(:function_expr) { none }
|
60
|
+
|
61
|
+
# Segments
|
62
|
+
rule(:segment) { child_segment | descendant_segment }
|
63
|
+
rule :child_segment do
|
64
|
+
(bracketed_selection | (str('.') >> (wildcard_selector | member_name_shorthand))).as :child
|
65
|
+
end
|
66
|
+
rule(:bracketed_selection) { str('[') >> some_separated(selector, symbol(',')).as(:bracketed) >> str(']') }
|
67
|
+
rule(:member_name_shorthand) { (match('[a-zA-Z_]') >> many(match('[a-zA-Z_0-9]'))).as(:name) }
|
68
|
+
rule :descendant_segment do
|
69
|
+
(str('..') >> (bracketed_selection | wildcard_selector | member_name_shorthand)).as :descendant
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require_relative '../multiple_values_returned_by_singular_query'
|
3
|
+
|
4
|
+
module JsonPath
|
5
|
+
module Parser
|
6
|
+
class Transformer < Parslet::Transform
|
7
|
+
EMPTY = ::Object.new
|
8
|
+
|
9
|
+
rule segments: sequence(:segments) do
|
10
|
+
proc do |root|
|
11
|
+
segments.reduce [root] do |nodes, segment|
|
12
|
+
segment.call nodes, root
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
rule(child: simple(:segment)) { segment }
|
18
|
+
|
19
|
+
rule descendant: simple(:segment) do
|
20
|
+
proc do |nodes, root|
|
21
|
+
recursive_application = proc do |node|
|
22
|
+
segment.call([node], root) + node.children.flat_map(&recursive_application)
|
23
|
+
end
|
24
|
+
|
25
|
+
nodes.flat_map(&recursive_application)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
rule(bracketed: simple(:segment)) { segment }
|
30
|
+
|
31
|
+
rule bracketed: sequence(:segments) do
|
32
|
+
proc do |nodes, root|
|
33
|
+
segments.flat_map { _1.call nodes, root }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
rule name: simple(:name) do
|
38
|
+
proc do |nodes|
|
39
|
+
nodes.filter_map do |node|
|
40
|
+
node.hash[name.to_s] if node.is_a? Nodes::Object
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
rule wildcard: simple(:x) do
|
46
|
+
proc do |nodes|
|
47
|
+
nodes.flat_map(&:children)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
rule index: simple(:index) do
|
52
|
+
proc do |nodes|
|
53
|
+
nodes.filter_map do |node|
|
54
|
+
node.elements[Integer(index)] if node.is_a? Nodes::Array
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
rule start: simple(:slice_start),
|
60
|
+
end: simple(:slice_end),
|
61
|
+
step: simple(:slice_step) do
|
62
|
+
proc do |nodes|
|
63
|
+
slice_step = Integer(self.slice_step) || 1
|
64
|
+
|
65
|
+
next [] if slice_step.zero?
|
66
|
+
|
67
|
+
nodes.flat_map do |node|
|
68
|
+
next [] unless node.is_a? Nodes::Array
|
69
|
+
|
70
|
+
len = node.elements.size
|
71
|
+
|
72
|
+
slice_start, slice_end = if slice_step.positive?
|
73
|
+
[
|
74
|
+
Integer(self.slice_start) || 0,
|
75
|
+
Integer(self.slice_end) || len
|
76
|
+
]
|
77
|
+
else
|
78
|
+
[
|
79
|
+
Integer(self.slice_start) || (len - 1),
|
80
|
+
Integer(self.slice_end) || (-len - 1)
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
slice_start += len if slice_start.negative?
|
85
|
+
slice_end += len if slice_end.negative?
|
86
|
+
|
87
|
+
lower, upper = if slice_step.positive?
|
88
|
+
[
|
89
|
+
[[slice_start, 0].max, len].min,
|
90
|
+
[[slice_end, 0].max, len].min
|
91
|
+
]
|
92
|
+
else
|
93
|
+
[
|
94
|
+
[[slice_end, -1].max, len - 1].min,
|
95
|
+
[[slice_start, -1].max, len - 1].min
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
# This is horrible, but it's also the easiest way to implement the semantics from the RFC
|
100
|
+
[].tap do |result|
|
101
|
+
if slice_step.positive?
|
102
|
+
i = lower
|
103
|
+
while i < upper
|
104
|
+
result << node.elements[i] if (0...node.elements.size).include? i
|
105
|
+
i += slice_step
|
106
|
+
end
|
107
|
+
else
|
108
|
+
i = upper
|
109
|
+
while i > lower
|
110
|
+
result << node.elements[i] if (0...node.elements.size).include? i
|
111
|
+
i += step
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
rule filter: simple(:filter) do
|
120
|
+
proc do |nodes, root|
|
121
|
+
nodes.flat_map do |node|
|
122
|
+
case node
|
123
|
+
when Nodes::Array
|
124
|
+
node.elements
|
125
|
+
when Nodes::Object
|
126
|
+
node.hash.values
|
127
|
+
else
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
.filter { filter.call root, _1 }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
rule(logical_or_operands: simple(:operand)) { operand }
|
136
|
+
rule(logical_and_operands: simple(:operand)) { operand }
|
137
|
+
|
138
|
+
rule logical_or_operands: sequence(:operands) do
|
139
|
+
proc do |*args, **kwargs, &block|
|
140
|
+
operands.any? { _1.call(*args, **kwargs, &block) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
rule logical_and_operands: sequence(:operands) do
|
145
|
+
proc do |*args, **kwargs, &block|
|
146
|
+
operands.all? { _1.call(*args, **kwargs, &block) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
rule negation: simple(:negation),
|
151
|
+
test_expr: simple(:expr) do
|
152
|
+
expr >> proc { negation ? !_1 : _1 }
|
153
|
+
end
|
154
|
+
|
155
|
+
rule filter_query: simple(:query) do
|
156
|
+
query >> proc { !_1.empty? }
|
157
|
+
end
|
158
|
+
|
159
|
+
rule negation: simple(:negation),
|
160
|
+
parenthesized_expr: simple(:expr) do
|
161
|
+
expr >> proc { negation ? !_1 : _1 }
|
162
|
+
end
|
163
|
+
|
164
|
+
rule comparable1: simple(:comp1),
|
165
|
+
comparison_op: simple(:op),
|
166
|
+
comparable2: simple(:comp2) do
|
167
|
+
proc do |*args, **kwargs, &block|
|
168
|
+
[comp1.call(*args, **kwargs, &block), comp2.call(*args, **kwargs, &block)]
|
169
|
+
end >> proc { _1.public_send op.to_s.strip.to_sym, _2 }
|
170
|
+
end
|
171
|
+
|
172
|
+
rule relative_segments: sequence(:segments) do
|
173
|
+
proc do |root, current_node|
|
174
|
+
segments.reduce [current_node] do |nodes, segment|
|
175
|
+
segment.call nodes, root
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
rule literal_number: simple(:num) do
|
181
|
+
proc { eval num }
|
182
|
+
end
|
183
|
+
|
184
|
+
rule literal_string: simple(:string) do
|
185
|
+
proc { string.to_s }
|
186
|
+
end
|
187
|
+
|
188
|
+
rule literal_true: simple(:x) do
|
189
|
+
proc { true }
|
190
|
+
end
|
191
|
+
|
192
|
+
rule literal_false: simple(:x) do
|
193
|
+
proc { false }
|
194
|
+
end
|
195
|
+
|
196
|
+
rule literal_null: simple(:x) do
|
197
|
+
proc {}
|
198
|
+
end
|
199
|
+
|
200
|
+
rule string: simple(:string) do
|
201
|
+
string
|
202
|
+
end
|
203
|
+
|
204
|
+
rule singular_query: simple(:query) do
|
205
|
+
proc do |*args, **kwargs, &block|
|
206
|
+
nodes = query.call(*args, **kwargs, &block)
|
207
|
+
|
208
|
+
if nodes.empty?
|
209
|
+
EMPTY
|
210
|
+
elsif nodes.size == 1
|
211
|
+
nodes.first.value
|
212
|
+
else
|
213
|
+
raise MultipleValuesReturnedBySingularQuery, 'Singular query must return single value'
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'parser/raw_parser'
|
2
|
+
require_relative 'parser/transformer'
|
3
|
+
|
4
|
+
module JsonPath
|
5
|
+
module Parser
|
6
|
+
RAW_PARSER = RawParser.new
|
7
|
+
TRANSFORMER = Transformer.new
|
8
|
+
|
9
|
+
def self.compile json_path_string
|
10
|
+
reporter = Parslet::ErrorReporter::Contextual.new
|
11
|
+
TRANSFORMER.apply RAW_PARSER.parse(json_path_string, reporter: reporter)
|
12
|
+
rescue Parslet::ParseFailed
|
13
|
+
reporter.deepest_cause.raise
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
|
3
|
+
module JsonPath
|
4
|
+
class Path
|
5
|
+
attr_reader :string
|
6
|
+
attr_reader :proc
|
7
|
+
|
8
|
+
def initialize json_path
|
9
|
+
if json_path.is_a? Path
|
10
|
+
@string = json_path.string
|
11
|
+
@proc = json_path.proc
|
12
|
+
else
|
13
|
+
@string = -json_path
|
14
|
+
@proc = Parser.compile json_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def apply node
|
19
|
+
proc.call node
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/json_path.rb
ADDED
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_path_rfc9535
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Moku S.r.l.
|
8
|
+
- Riccardo Agatea
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2024-09-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: parslet
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.0'
|
28
|
+
description: Like XPath is a query language for XML, JsonPath is a query language
|
29
|
+
for JSON. This gem aims to be an implementation of RFC 9535. Unlike tha original
|
30
|
+
JsonPath description (http://goessner.net/articles/JsonPath/), RFC 9535 is strictly
|
31
|
+
normative, which ideally should leave open fewer doors for inconsistencies.
|
32
|
+
email:
|
33
|
+
- info@moku.io
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- ".rubocop.yml"
|
39
|
+
- CHANGELOG.md
|
40
|
+
- README.md
|
41
|
+
- lib/json_path.rb
|
42
|
+
- lib/json_path/doc.rb
|
43
|
+
- lib/json_path/error.rb
|
44
|
+
- lib/json_path/multiple_values_returned_by_singular_query.rb
|
45
|
+
- lib/json_path/node_list.rb
|
46
|
+
- lib/json_path/nodes.rb
|
47
|
+
- lib/json_path/nodes/array.rb
|
48
|
+
- lib/json_path/nodes/base.rb
|
49
|
+
- lib/json_path/nodes/false.rb
|
50
|
+
- lib/json_path/nodes/null.rb
|
51
|
+
- lib/json_path/nodes/number.rb
|
52
|
+
- lib/json_path/nodes/object.rb
|
53
|
+
- lib/json_path/nodes/string.rb
|
54
|
+
- lib/json_path/nodes/true.rb
|
55
|
+
- lib/json_path/parser.rb
|
56
|
+
- lib/json_path/parser/core.rb
|
57
|
+
- lib/json_path/parser/raw_parser.rb
|
58
|
+
- lib/json_path/parser/transformer.rb
|
59
|
+
- lib/json_path/path.rb
|
60
|
+
- lib/json_path/unrecognized_node.rb
|
61
|
+
- lib/json_path_rfc9535.rb
|
62
|
+
- lib/json_path_rfc9535/version.rb
|
63
|
+
homepage: https://github.com/moku-io/json_path_rfc9535
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata:
|
67
|
+
homepage_uri: https://github.com/moku-io/json_path_rfc9535
|
68
|
+
source_code_uri: https://github.com/moku-io/json_path_rfc9535
|
69
|
+
changelog_uri: https://github.com/moku-io/json_path_rfc9535/blob/master/CHANGELOG.md
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 3.0.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.5.11
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: A Ruby implementation of RFC 9535.
|
89
|
+
test_files: []
|