CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-oboe

Progressive JSON streaming parser that enables processing data as it arrives over HTTP without waiting for the complete response

94

1.11x
Overview
Eval results
Files

jsonpath-patterns.mddocs/

JSONPath Pattern Matching

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.

Capabilities

Basic Pattern Syntax

Root Pattern

Match the root JSON object or array.

// Pattern: "!"
// Matches: The root node of the JSON structure

Usage Examples:

// Match root object
oboe('https://api.example.com/user.json')
  .node('!', function(root) {
    console.log('Root object:', root);
    // For JSON: {"name": "John", "age": 30}
    // Outputs: {name: "John", age: 30}
  });

// Root array
oboe('https://api.example.com/users.json')
  .node('!', function(root) {
    console.log('Root array:', root);
    // For JSON: [{"name": "John"}, {"name": "Jane"}]
    // Outputs: [{name: "John"}, {name: "Jane"}]
  });

Property Access

Match specific properties of objects.

// Pattern: "!.propertyName"
// Matches: Named property of root object

// Pattern: "!.parent.child"  
// Matches: Nested property access

Usage Examples:

// Direct property access
oboe('https://api.example.com/user.json')
  .node('!.name', function(name) {
    console.log('User name:', name);
    // For JSON: {"name": "John", "age": 30}
    // Outputs: "John"
  });

// Nested property access
oboe('https://api.example.com/profile.json')
  .node('!.user.profile.email', function(email) {
    console.log('Email:', email);
    // For JSON: {"user": {"profile": {"email": "john@example.com"}}}
    // Outputs: "john@example.com"
  });

Wildcard Patterns

Any Property Wildcard

Match any property at a given level using the * wildcard.

// Pattern: "!.*"
// Matches: Any property of the root object

// Pattern: "!.users.*"
// Matches: Any property of the users object

Usage Examples:

// Any root property
oboe('https://api.example.com/data.json')
  .node('!.*', function(value, path) {
    console.log('Property', path[0], ':', value);
    // For JSON: {"users": [...], "posts": [...], "count": 42}
    // Outputs multiple times:
    // Property users : [...]
    // Property posts : [...]  
    // Property count : 42
  });

// Any user object
oboe('https://api.example.com/data.json')
  .node('!.users.*', function(user, path) {
    console.log('User at index', path[1], ':', user.name);
    // For JSON: {"users": [{"name": "John"}, {"name": "Jane"}]}
    // Outputs:
    // User at index 0 : John
    // User at index 1 : Jane
  });

Array Access Patterns

Array Element Access

Access specific array elements or all elements.

// Pattern: "![*]"
// Matches: Any element of root array

// Pattern: "![0]"  
// Matches: First element of root array

// Pattern: "!.users[*]"
// Matches: Any element of users array

// Pattern: "!.users[2]"
// Matches: Third element (index 2) of users array

Usage Examples:

// Any array element
oboe('https://api.example.com/users.json')
  .node('![*]', function(user, path) {
    console.log('User at index', path[0], ':', user);
    // For JSON: [{"name": "John"}, {"name": "Jane"}]
    // Outputs:
    // User at index 0 : {name: "John"}
    // User at index 1 : {name: "Jane"}
  });

// Specific array element
oboe('https://api.example.com/users.json')
  .node('![0]', function(firstUser) {
    console.log('First user:', firstUser);
    // Only matches the first element
  });

// Nested array access
oboe('https://api.example.com/data.json')
  .node('!.departments.*.employees[*]', function(employee, path) {
    console.log('Employee in dept', path[1], ':', employee.name);
    // Matches employees in any department
  });

Bracket Notation

Use bracket notation for property names that contain special characters.

// Pattern: '!["property-name"]' 
// Matches: Property with hyphens or special characters

// Pattern: '!.users[*]["full-name"]'
// Matches: Property with special characters in array elements

Usage Examples:

// Special character properties
oboe('https://api.example.com/data.json')
  .node('!["content-type"]', function(contentType) {
    console.log('Content type:', contentType);
    // For JSON: {"content-type": "application/json"}
  });

// Mixed notation
oboe('https://api.example.com/users.json')
  .node('!.users[*]["first-name"]', function(firstName, path) {
    console.log('First name of user', path[1], ':', firstName);
    // For JSON: {"users": [{"first-name": "John"}]}
  });

Field Selection Patterns

Field Filtering

Select only specific fields from matched objects using field selection syntax.

// Pattern: "!{field1 field2}"
// Matches: Root object with only specified fields

// Pattern: "!.users.*{name email}"  
// Matches: User objects with only name and email fields

Usage Examples:

// Root object field selection
oboe('https://api.example.com/user.json')
  .node('!{name email}', function(user) {
    console.log('Selected fields:', user);
    // For JSON: {"name": "John", "age": 30, "email": "john@example.com"}
    // Outputs: {name: "John", email: "john@example.com"}
    // (age field excluded)
  });

// Array element field selection  
oboe('https://api.example.com/users.json')
  .node('!.users.*{name}', function(user) {
    console.log('User name only:', user);
    // For JSON: {"users": [{"name": "John", "age": 30, "city": "NYC"}]}
    // Outputs: {name: "John"}
    // (age and city fields excluded)
  });

// Multiple field selection
oboe('https://api.example.com/products.json')
  .node('!.products.*{id title price}', function(product) {
    console.log('Product summary:', product);
    // Only id, title, and price fields included
  });

Capturing Patterns

Pattern Capturing

Use the $ prefix to capture matched values for later reference.

// Pattern: "$!.users.*"
// Captures: Each matched user object

// Pattern: "$!.users.*.name"
// Captures: Each matched user name

Usage Examples:

// Capture objects
oboe('https://api.example.com/users.json')
  .node('$!.users.*', function(user, path, ancestors) {
    console.log('Captured user:', user);
    // The user object is captured and can be referenced
  });

// Capture specific values
oboe('https://api.example.com/data.json')
  .node('$!.config.settings.*', function(setting, path) {
    console.log('Captured setting', path[2], ':', setting);  
    // Each configuration setting value is captured
  });

Complex Pattern Examples

Multi-Level Matching

Combine various pattern elements for complex matching.

// Pattern: "!.departments.*.teams[*].members.*{name role}"
// Matches: Team member objects with name and role fields, 
//          from any team in any department

Complex Usage Examples:

// Deep nested structure matching
oboe('https://api.example.com/organization.json')
  .node('!.departments.*.teams[*].members.*', function(member, path) {
    const deptName = path[1];
    const teamIndex = path[3]; 
    const memberIndex = path[5];
    console.log(`Member ${memberIndex} in team ${teamIndex} of ${deptName}:`, member);
  });

// Multiple wildcard levels
oboe('https://api.example.com/catalog.json')
  .node('!.categories.*.products[*].variants.*', function(variant, path) {
    console.log('Product variant:', variant);
    console.log('Category:', path[1]);
    console.log('Product index:', path[3]);
    console.log('Variant index:', path[5]);
  });

// Mixed patterns with field selection
oboe('https://api.example.com/social.json')
  .node('!.users.*.posts[*].comments.*{author text timestamp}', function(comment) {
    console.log('Comment summary:', comment);
    // Only author, text, and timestamp fields included
  });

Path vs Node Patterns

Path Patterns

Use with .path() to match JSON structure paths rather than values.

// Matches when the path structure becomes available,
// not when the value is complete

Path Pattern Examples:

// Monitor structure discovery
oboe('https://api.example.com/data.json')
  .path('!.users', function(path, ancestors) {
    console.log('Users array discovered');
    // Called when "users" property is found, before its contents
  })
  .path('!.users.*', function(path, ancestors) {
    console.log('User object at path:', path);
    // Called for each user path: ['users', '0'], ['users', '1'], etc.
  });

// Deep path monitoring
oboe('https://api.example.com/complex.json')
  .path('!.data.results[*].metadata', function(path) {
    console.log('Metadata path found:', path);
    // Called when metadata object structure is discovered
  });

Pattern Matching Rules

Evaluation Order

Patterns are evaluated as the JSON structure is discovered:

  1. Path patterns fire when structure paths are discovered
  2. Node patterns fire when complete values are available
  3. Wildcards match in discovery order
  4. Field selection applies filtering after matching

Performance Considerations

  • Specific patterns are more efficient than wildcards
  • Shallow patterns perform better than deep nested patterns
  • Field selection reduces memory usage for large objects
  • Early matching allows processing before complete JSON is received

Pattern Debugging

Use path information in callbacks to understand matching:

oboe('https://api.example.com/data.json')
  .node('!.users.*', function(user, path, ancestors) {
    console.log('Pattern matched:');
    console.log('  Value:', user);
    console.log('  Path:', path);              // ['users', '0']
    console.log('  Ancestors:', ancestors);    // [rootObject, usersArray]
  });

Install with Tessl CLI

npx tessl i tessl/npm-oboe

docs

factory.md

index.md

instance-api.md

jsonpath-patterns.md

stream-processing.md

tile.json