0
# JSONPath Pattern Matching
1
2
Oboe.js uses a powerful JSONPath-style pattern matching system for selecting specific nodes and paths in the JSON stream. Patterns allow you to specify exactly which parts of the JSON structure you're interested in, enabling precise control over event triggering.
3
4
## Capabilities
5
6
### Basic Pattern Syntax
7
8
#### Root Pattern
9
10
Match the root JSON object or array.
11
12
```javascript { .api }
13
// Pattern: "!"
14
// Matches: The root node of the JSON structure
15
```
16
17
**Usage Examples:**
18
19
```javascript
20
// Match root object
21
oboe('https://api.example.com/user.json')
22
.node('!', function(root) {
23
console.log('Root object:', root);
24
// For JSON: {"name": "John", "age": 30}
25
// Outputs: {name: "John", age: 30}
26
});
27
28
// Root array
29
oboe('https://api.example.com/users.json')
30
.node('!', function(root) {
31
console.log('Root array:', root);
32
// For JSON: [{"name": "John"}, {"name": "Jane"}]
33
// Outputs: [{name: "John"}, {name: "Jane"}]
34
});
35
```
36
37
#### Property Access
38
39
Match specific properties of objects.
40
41
```javascript { .api }
42
// Pattern: "!.propertyName"
43
// Matches: Named property of root object
44
45
// Pattern: "!.parent.child"
46
// Matches: Nested property access
47
```
48
49
**Usage Examples:**
50
51
```javascript
52
// Direct property access
53
oboe('https://api.example.com/user.json')
54
.node('!.name', function(name) {
55
console.log('User name:', name);
56
// For JSON: {"name": "John", "age": 30}
57
// Outputs: "John"
58
});
59
60
// Nested property access
61
oboe('https://api.example.com/profile.json')
62
.node('!.user.profile.email', function(email) {
63
console.log('Email:', email);
64
// For JSON: {"user": {"profile": {"email": "john@example.com"}}}
65
// Outputs: "john@example.com"
66
});
67
```
68
69
### Wildcard Patterns
70
71
#### Any Property Wildcard
72
73
Match any property at a given level using the `*` wildcard.
74
75
```javascript { .api }
76
// Pattern: "!.*"
77
// Matches: Any property of the root object
78
79
// Pattern: "!.users.*"
80
// Matches: Any property of the users object
81
```
82
83
**Usage Examples:**
84
85
```javascript
86
// Any root property
87
oboe('https://api.example.com/data.json')
88
.node('!.*', function(value, path) {
89
console.log('Property', path[0], ':', value);
90
// For JSON: {"users": [...], "posts": [...], "count": 42}
91
// Outputs multiple times:
92
// Property users : [...]
93
// Property posts : [...]
94
// Property count : 42
95
});
96
97
// Any user object
98
oboe('https://api.example.com/data.json')
99
.node('!.users.*', function(user, path) {
100
console.log('User at index', path[1], ':', user.name);
101
// For JSON: {"users": [{"name": "John"}, {"name": "Jane"}]}
102
// Outputs:
103
// User at index 0 : John
104
// User at index 1 : Jane
105
});
106
```
107
108
### Array Access Patterns
109
110
#### Array Element Access
111
112
Access specific array elements or all elements.
113
114
```javascript { .api }
115
// Pattern: "![*]"
116
// Matches: Any element of root array
117
118
// Pattern: "![0]"
119
// Matches: First element of root array
120
121
// Pattern: "!.users[*]"
122
// Matches: Any element of users array
123
124
// Pattern: "!.users[2]"
125
// Matches: Third element (index 2) of users array
126
```
127
128
**Usage Examples:**
129
130
```javascript
131
// Any array element
132
oboe('https://api.example.com/users.json')
133
.node('![*]', function(user, path) {
134
console.log('User at index', path[0], ':', user);
135
// For JSON: [{"name": "John"}, {"name": "Jane"}]
136
// Outputs:
137
// User at index 0 : {name: "John"}
138
// User at index 1 : {name: "Jane"}
139
});
140
141
// Specific array element
142
oboe('https://api.example.com/users.json')
143
.node('![0]', function(firstUser) {
144
console.log('First user:', firstUser);
145
// Only matches the first element
146
});
147
148
// Nested array access
149
oboe('https://api.example.com/data.json')
150
.node('!.departments.*.employees[*]', function(employee, path) {
151
console.log('Employee in dept', path[1], ':', employee.name);
152
// Matches employees in any department
153
});
154
```
155
156
#### Bracket Notation
157
158
Use bracket notation for property names that contain special characters.
159
160
```javascript { .api }
161
// Pattern: '!["property-name"]'
162
// Matches: Property with hyphens or special characters
163
164
// Pattern: '!.users[*]["full-name"]'
165
// Matches: Property with special characters in array elements
166
```
167
168
**Usage Examples:**
169
170
```javascript
171
// Special character properties
172
oboe('https://api.example.com/data.json')
173
.node('!["content-type"]', function(contentType) {
174
console.log('Content type:', contentType);
175
// For JSON: {"content-type": "application/json"}
176
});
177
178
// Mixed notation
179
oboe('https://api.example.com/users.json')
180
.node('!.users[*]["first-name"]', function(firstName, path) {
181
console.log('First name of user', path[1], ':', firstName);
182
// For JSON: {"users": [{"first-name": "John"}]}
183
});
184
```
185
186
### Field Selection Patterns
187
188
#### Field Filtering
189
190
Select only specific fields from matched objects using field selection syntax.
191
192
```javascript { .api }
193
// Pattern: "!{field1 field2}"
194
// Matches: Root object with only specified fields
195
196
// Pattern: "!.users.*{name email}"
197
// Matches: User objects with only name and email fields
198
```
199
200
**Usage Examples:**
201
202
```javascript
203
// Root object field selection
204
oboe('https://api.example.com/user.json')
205
.node('!{name email}', function(user) {
206
console.log('Selected fields:', user);
207
// For JSON: {"name": "John", "age": 30, "email": "john@example.com"}
208
// Outputs: {name: "John", email: "john@example.com"}
209
// (age field excluded)
210
});
211
212
// Array element field selection
213
oboe('https://api.example.com/users.json')
214
.node('!.users.*{name}', function(user) {
215
console.log('User name only:', user);
216
// For JSON: {"users": [{"name": "John", "age": 30, "city": "NYC"}]}
217
// Outputs: {name: "John"}
218
// (age and city fields excluded)
219
});
220
221
// Multiple field selection
222
oboe('https://api.example.com/products.json')
223
.node('!.products.*{id title price}', function(product) {
224
console.log('Product summary:', product);
225
// Only id, title, and price fields included
226
});
227
```
228
229
### Capturing Patterns
230
231
#### Pattern Capturing
232
233
Use the `$` prefix to capture matched values for later reference.
234
235
```javascript { .api }
236
// Pattern: "$!.users.*"
237
// Captures: Each matched user object
238
239
// Pattern: "$!.users.*.name"
240
// Captures: Each matched user name
241
```
242
243
**Usage Examples:**
244
245
```javascript
246
// Capture objects
247
oboe('https://api.example.com/users.json')
248
.node('$!.users.*', function(user, path, ancestors) {
249
console.log('Captured user:', user);
250
// The user object is captured and can be referenced
251
});
252
253
// Capture specific values
254
oboe('https://api.example.com/data.json')
255
.node('$!.config.settings.*', function(setting, path) {
256
console.log('Captured setting', path[2], ':', setting);
257
// Each configuration setting value is captured
258
});
259
```
260
261
### Complex Pattern Examples
262
263
#### Multi-Level Matching
264
265
Combine various pattern elements for complex matching.
266
267
```javascript { .api }
268
// Pattern: "!.departments.*.teams[*].members.*{name role}"
269
// Matches: Team member objects with name and role fields,
270
// from any team in any department
271
```
272
273
**Complex Usage Examples:**
274
275
```javascript
276
// Deep nested structure matching
277
oboe('https://api.example.com/organization.json')
278
.node('!.departments.*.teams[*].members.*', function(member, path) {
279
const deptName = path[1];
280
const teamIndex = path[3];
281
const memberIndex = path[5];
282
console.log(`Member ${memberIndex} in team ${teamIndex} of ${deptName}:`, member);
283
});
284
285
// Multiple wildcard levels
286
oboe('https://api.example.com/catalog.json')
287
.node('!.categories.*.products[*].variants.*', function(variant, path) {
288
console.log('Product variant:', variant);
289
console.log('Category:', path[1]);
290
console.log('Product index:', path[3]);
291
console.log('Variant index:', path[5]);
292
});
293
294
// Mixed patterns with field selection
295
oboe('https://api.example.com/social.json')
296
.node('!.users.*.posts[*].comments.*{author text timestamp}', function(comment) {
297
console.log('Comment summary:', comment);
298
// Only author, text, and timestamp fields included
299
});
300
```
301
302
### Path vs Node Patterns
303
304
#### Path Patterns
305
306
Use with `.path()` to match JSON structure paths rather than values.
307
308
```javascript { .api }
309
// Matches when the path structure becomes available,
310
// not when the value is complete
311
```
312
313
**Path Pattern Examples:**
314
315
```javascript
316
// Monitor structure discovery
317
oboe('https://api.example.com/data.json')
318
.path('!.users', function(path, ancestors) {
319
console.log('Users array discovered');
320
// Called when "users" property is found, before its contents
321
})
322
.path('!.users.*', function(path, ancestors) {
323
console.log('User object at path:', path);
324
// Called for each user path: ['users', '0'], ['users', '1'], etc.
325
});
326
327
// Deep path monitoring
328
oboe('https://api.example.com/complex.json')
329
.path('!.data.results[*].metadata', function(path) {
330
console.log('Metadata path found:', path);
331
// Called when metadata object structure is discovered
332
});
333
```
334
335
## Pattern Matching Rules
336
337
### Evaluation Order
338
339
Patterns are evaluated as the JSON structure is discovered:
340
341
1. **Path patterns** fire when structure paths are discovered
342
2. **Node patterns** fire when complete values are available
343
3. **Wildcards** match in discovery order
344
4. **Field selection** applies filtering after matching
345
346
### Performance Considerations
347
348
- **Specific patterns** are more efficient than wildcards
349
- **Shallow patterns** perform better than deep nested patterns
350
- **Field selection** reduces memory usage for large objects
351
- **Early matching** allows processing before complete JSON is received
352
353
### Pattern Debugging
354
355
Use path information in callbacks to understand matching:
356
357
```javascript
358
oboe('https://api.example.com/data.json')
359
.node('!.users.*', function(user, path, ancestors) {
360
console.log('Pattern matched:');
361
console.log(' Value:', user);
362
console.log(' Path:', path); // ['users', '0']
363
console.log(' Ancestors:', ancestors); // [rootObject, usersArray]
364
});
365
```