JSONPath & Attribute Resolution Guide

JSONPath & Attribute Resolution Guide

JSONPath is the only "coding-like" element in Decisimo's no-code platform. It provides a standardized way to access data attributes in your JSON data objects across all decision logic components.

Quick Reference


Basic JSONPath Syntax

JSONPath provides a standardized way to navigate through JSON data structures:

PatternDescriptionExample
$Root object$
$.fieldDirect property$.customer_name
$.nested.fieldNested property$.customer.personal.age
$.array[0]Array element by index$.transactions[0].amount
$.array[*]All array elements$.accounts[*].balance
$.array.lengthArray length$.transactions.length

Basic Example

Given this JSON data object:

{
  "customer": {
    "name": "John Doe",
    "age": 45,
    "credit_score": 720
  },
  "loan": {
    "amount": 50000,
    "term": 60,
    "purpose": "home_improvement"
  },
  "transactions": [
    { "date": "2024-01-15", "amount": 1200 },
    { "date": "2024-02-10", "amount": 850 }
  ],
  "last_update": "2024-03-15"
}

JSONPath Examples:

  • $.customer.name → "John Doe"
  • $.customer.age → 45
  • $.loan.amount → 50000
  • $.transactions[0].amount → 1200
  • $.transactions.length → 2
  • $.last_update → "2024-03-15"

Common Attribute Patterns

Customer Data

$.customer.id           // Customer identifier
$.customer.name         // Customer full name
$.customer.email        // Contact email
$.customer.age          // Customer age
$.customer.credit_score // Credit rating
$.customer.income       // Monthly income
$.customer.employment   // Employment status

Financial Data

$.loan.amount           // Loan amount requested
$.loan.term             // Loan term in months
$.loan.purpose          // Purpose of loan
$.loan.interest_rate    // Interest rate
$.income.monthly        // Monthly income
$.expenses.total        // Total monthly expenses
$.debt.total            // Total debt amount
$.debt_ratio            // Debt-to-income ratio

Transaction & Account Data

$.transactions[0].amount     // First transaction amount
$.transactions[*].type       // All transaction types
$.account.balance            // Current account balance
$.account.type               // Account type
$.payment_history.status     // Payment history status
$.payment_history.late_count // Number of late payments

Component-Specific Usage

Rule Sets

Conditions: Use attribute paths to specify what data to evaluate

$.loan_amount > 50000
$.customer.credit_score >= 700
$.application_date Time > "2024-01-01"

Actions: Use attribute paths to specify where to store results

$.approval_status = "APPROVED"
$.risk_score = 85
$.approver = "John Smith"

Decision Tables

Column Conditions: Each column uses an attribute path for evaluation

Age Column: $.customer.age
Income Column: $.monthly_income  
Credit Column: $.credit_score

Result Columns (column-level): Define where to store the table's decision

Decision Column: $.loan_decision
Rate Column: $.interest_rate

Result Cells (cell-level): Each result cell can use a dynamic attribute path instead of a static value. When configured in attribute mode, the cell reads its value from the specified path in the input data at runtime. This allows individual cells within the same column to pull values from different data fields, making the table more flexible. See Decision Tables — Dynamic Result Values for details.

Scorecards

Predictor Attributes: Specify which data fields to score

Credit Score Predictor: $.credit_score
Income Predictor: $.monthly_income
Debt Ratio Predictor: $.debt_ratio

Output Attribute: Define where to store the final score

Scorecard Output: $.risk_score
Scorecard Output: $.profit_margin

Decision Trees

Node Conditions: Attribute paths for decision points

$.credit_score >= 700     // Branch condition
$.customer.age < 25       // Age-based branching
$.loan.amount > 100000    // Amount thresholds

Node Actions: Where to store results at endpoints

$.loan_decision = "APPROVED"    // Final decision
$.risk_category = "LOW"         // Risk classification

Data Type Handling

The Decisimo evaluation engine intelligently handles different data types:

Numeric Values

The ordering operators (>, ≥, <, ≤) and loose equality (≈) read all of these as numbers:

1000
"1000"
"1,000.00"    // Thousands separators removed
"$1,000.00"   // Currency symbols removed
"€1.000,50"   // International formats supported

Strict equality (=) compares the values as written, so "1,000.00" = 1000 is false. Use ≈ to compare formatted amounts for equality.

String Values

String operations are Unicode-aware and case-sensitive. Lengths and slices count characters, so "José".length is 4:

$.name = "José García"           // Unicode support
$.status = "ACTIVE"              // Case-sensitive
$.category = "premium"           // Lowercase

Boolean Values

$.is_verified = true
$.has_defaults = false
$.opt_in_marketing = true

Arrays and Collections

$.transactions.length            // Count of items
$.transactions[0].amount         // First item
$.transactions[-1].amount        // Last item
$.accounts["savings"].balance    // Object member by quoted key

A path that names many values, such as $.transactions[*].amount, returns a list. A condition needs a list operator for it (array contains, array value contains and their variants) or an iterative rule that checks one item at a time. An ordinary operator such as > never matches a list. A filter that happens to match exactly one item returns that item's value on its own, so write conditions that still hold when the filter matches several.


Advanced JSONPath Features

Beyond basic attribute access, Decisimo's JSONPath implementation supports sophisticated data navigation and filtering techniques.

The .. operator searches recursively through all levels of your data structure:

// Find 'amount' at any level in the data
$..amount

// Example data:
{
  "loan": { "amount": 50000 },
  "transactions": [
    { "details": { "amount": 1200 } },
    { "amount": 850 }
  ]
}

// $..amount returns: [50000, 1200, 850]

Use Cases:

  • Finding all instances of a field across complex nested structures
  • Searching transaction amounts in varying data formats
  • Locating risk scores at different hierarchy levels

Array Slicing & Ranges

Extract specific portions of arrays using slice notation:

// Basic slicing
$.transactions[0:3]      // First 3 transactions
$.transactions[1:]       // All except first
$.transactions[:2]       // First 2 transactions
$.transactions[-2:]      // Last 2 transactions

// With step values (every nth item)
$.transactions[0::2]     // Every other transaction (0, 2, 4...)
$.transactions[1:10:3]   // Items 1, 4, 7 from range 1-10

Real-World Examples:

// Recent payment history (last 6 months)
$.payment_history[-6:]

// Quarterly data points
$.monthly_income[0::3]   // Every 3rd month

// Sample transactions for analysis
$.transactions[0:5]      // First 5 for review

String Slicing & Manipulation

Extract parts of string values using slice notation:

// String slicing
$.customer.ssn[7:11]     // Last 4 digits: "1234"
$.account.routing[:5]    // First 5 characters
$.customer.name[0:1]     // First initial

// Example data:
{
  "customer": {
    "ssn": "123-45-1234",
    "name": "John Doe"
  }
}

// Results:
// $.customer.ssn[7:11] → "1234"
// $.customer.name[0:1] → "J"

Complex Array Filtering

Filters follow RFC 9535. @ is the item being tested and $ is the whole data object. Combine tests with &&, || and !, group them with parentheses, and compare with ==, !=, <, <=, > and >=. A filter may span several lines.

// Boolean expression filtering
$.transactions[?(@.amount > 1000)]              // High-value transactions
$.accounts[?(@.balance > 0 && @.status == "ACTIVE")]  // Active accounts with balance
$.payments[?(@.date >= "2024-01-01" || @.amount > 500)] // Recent OR large payments

// Multiple conditions with logical operators
$.loans[?(@.status == "ACTIVE" && @.balance > 10000 && @.rate < 5.0)]

// Comparing with another value in the data object
$.transactions[?(@.amount > $.spending.threshold)]

// Pattern matching
$.customers[?(match(@.email, ".*@company\\.com"))]   // Whole value matches
$.transactions[?(search(@.merchant, "air"))]          // Value contains a match

The parentheses around the whole filter are optional: $.transactions[?@.amount > 1000] is the same filter. The words and, or and not, and the regex comparison @.email =~ /pattern/, are also accepted.

A string and a number never compare as ordered, with one exception: a number stored as text, such as "2500", compares as a number.

Filter Functions

FunctionReturnsExample
length(value)Characters in a string, items in a list, members of an object$.customers[?(length(@.name) > 20)]
count(query)How many values a query selects$.customers[?(count(@.late_payments[-12:]) > 3)]
match(value, regex)True when the whole value matches$.accounts[?(match(@.iban, "CZ[0-9]{22}"))]
search(value, regex)True when part of the value matches$.transactions[?(search(@.merchant, "casino"))]
value(query)The single value a query selects$.orders[?(value(@.items[0].sku) == "A1")]

Advanced Filtering Examples:

// Risk assessment filtering
$.applications[?(@.credit_score >= 650 && @.income > 50000 && @.debt_ratio < 0.4)]

// Transaction anomaly detection
$.transactions[?(@.amount > 10000 || (@.location != @.previous.location && @.amount > 1000))]

// Customer segmentation
$.customers[?(@.age >= 25 && @.age <= 65 && @.account_value > 100000)]

Multi-Field Selection

Select multiple fields simultaneously using comma-separated lists:

// Multiple array indices
$.transactions[0,2,4]               // 1st, 3rd, and 5th transactions
$.accounts["checking","savings"]     // Specific account types

// Mixed field selection
$.customer["name","email","phone"]   // Contact information bundle

// Example result:
// Returns array with selected values: ["John Doe", "john@email.com", "555-1234"]

Length & Counting Operations

Get counts and lengths of arrays and strings:

// Array lengths
$.transactions.length               // Number of transactions
$.customer.addresses.length        // Number of addresses

// String lengths  
$.customer.name.length             // Length of name
$.customer.comments.length         // Comment character count

// Nested length operations
$.accounts[*].transactions.length   // Transaction count per account

.length applies to each value the path selects. $.transactions[?(@.category == "gambling")].length therefore returns the size of each matching transaction, not how many matched. To count matches, use count() inside a filter, or an iterative rule that adds 1 per matching item.

Negation & Conditional Logic

Use NOT operations and complex boolean logic:

// Negation
$.accounts[?(!@.closed)]                       // Open accounts
$.applications[?(!(@.status == "REJECTED"))]   // Non-rejected applications
$.customers[?(!(@.age < 18 || @.age > 65))]    // Working age customers

// Complex conditional chains
$.loans[?((@.type == "AUTO" && @.amount < 50000) || (@.type == "HOME" && @.ltv < 0.8))]

A bare @.field in a filter tests its value: !@.closed keeps accounts where closed is missing, false, zero or empty.

Wildcard Operations

Use wildcards for flexible data access:

// All fields at a level
$.customer.*                       // All customer fields
$.accounts.*.balance              // All account balances
$.risk_factors.*                  // All risk factor values

// All array elements
$.transactions[*].amount          // All transaction amounts
$.customers[*].credit_score       // All customer credit scores

// Combined with other operations
$.accounts[*].transactions[0:3]   // First 3 transactions per account

Practical Advanced Examples

Customer Risk Profiling

// High-risk customer identification
$.customers[?(
  @.credit_score < 600 &&
  @.debt_ratio > 0.5 &&
  count(@.late_payments[-12:]) > 3
)]

Transaction Pattern Analysis

// Unusual spending pattern detection.
// A function step first writes $.spending.threshold = monthly_average * 3.
$.transactions[?(
  @.amount > $.spending.threshold &&
  !match(@.merchant_category, "grocery|fuel|utilities")
)]

Multi-Product Scoring

// Cross-product risk assessment
$..risk_scores[?(@ > 750)]        // All high risk scores across products
$.products[*].applications[?(@.status == "PENDING" && @.score >= 700)]

Dynamic Thresholds

// Income-based qualification with dynamic thresholds.
// A function step first writes $.market.income_floor = median_income * 1.2
// and $.market.score_floor = average_score - 50.
$.applicants[?(
  @.monthly_income > $.market.income_floor &&
  @.employment_tenure >= 24 &&
  @.credit_score >= $.market.score_floor
)]

What Filters Do Not Support

  • Arithmetic. @.amount > ($.average * 3) is not valid. Work the number out in a function step, write it to the data object, and compare with the path it was written to.
  • in and not in. Use match() with alternatives, such as match(@.category, "grocery|fuel"), or a list operator in the condition.

A path the engine cannot read selects nothing. Releasing a decision flow that contains one raises the warning jsonpath_syntax_unsupported and names the step and the path.

Performance Considerations

Efficient Patterns:

  • Use specific paths when possible: $.customer.name vs $..name
  • Filter early in the path: $.active_customers[?(@.score > 700)].loans
  • Limit deep recursive searches to necessary cases

Resource-Intensive Patterns:

  • Recursive searches on large datasets: $..field
  • Complex filtering with multiple OR conditions
  • String pattern matching on large text fields

Common Problems

1. Path Not Found

// Problem: Typo in field name
$.custmer.name           // ❌ Misspelled

// Solution: Check spelling
$.customer.name          // ✅ Correct

2. Incorrect Array Access

// Problem: an index past the end
$.transactions[5].amount  // ❌ Nothing, when there are 5 transactions

// Solution: count from the end
$.transactions[-1].amount // ✅ The last transaction

3. Case Sensitivity

// Problem: Wrong case
$.Customer.Name          // ❌ Wrong case

// Solution: Match exact case
$.customer.name          // ✅ Correct

Debugging Tips

  1. Test with Simple Paths First - Start with $.field_name before going deeper
  2. Use Data Object Testing - Test your JSONPath with actual data objects
  3. Check Data Structure - Verify your JSON structure matches expected paths
  4. Use Verbose Output - Enable verbose execution to see attribute resolution details

Best Practices

Path Naming

✅ Good Examples:

$.customer.credit_score
$.loan.amount
$.risk.assessment.score

❌ Avoid:

$.Customer.Credit_Score  // Inconsistent casing
$.loan-amount           // Hyphens can be problematic  
$['field with spaces']  // Spaces require brackets

Data Structure Design

  • Keep paths reasonably shallow (3-4 levels max when possible)
  • Use consistent naming conventions across your data objects
  • Group related fields logically (e.g., all customer data under $.customer)
  • Avoid special characters in field names when possible

Testing JSONPath Expressions

Online Testing Tools

You can test JSONPath expressions using online tools:

Within Decisimo

  1. Object Testing: Use sample data objects to test your paths
  2. Verbose Output: Enable detailed execution logs
  3. Unit Testing: Test individual conditions with specific values


Need Help? If you encounter issues with JSONPath attribute resolution, refer to the troubleshooting section above or check component-specific documentation for implementation examples.