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 Syntax - Fundamental JSONPath patterns and rules
- Common Patterns - Real-world examples for different data types
- Component Usage - How each component uses attribute paths
- Data Type Handling - How the engine processes different value types
- Advanced Features - Recursive search, slicing, filtering & complex logic
- Troubleshooting - Common problems and solutions
Basic JSONPath Syntax
JSONPath provides a standardized way to navigate through JSON data structures:
| Pattern | Description | Example |
|---|---|---|
$ | Root object | $ |
$.field | Direct property | $.customer_name |
$.nested.field | Nested property | $.customer.personal.age |
$.array[0] | Array element by index | $.transactions[0].amount |
$.array[*] | All array elements | $.accounts[*].balance |
$.array.length | Array 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.
Recursive Deep Search
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
| Function | Returns | Example |
|---|---|---|
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. inandnot in. Usematch()with alternatives, such asmatch(@.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.namevs$..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
- Test with Simple Paths First - Start with
$.field_namebefore going deeper - Use Data Object Testing - Test your JSONPath with actual data objects
- Check Data Structure - Verify your JSON structure matches expected paths
- 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:
- JSONPath.com - Interactive JSONPath testing
Within Decisimo
- Object Testing: Use sample data objects to test your paths
- Verbose Output: Enable detailed execution logs
- Unit Testing: Test individual conditions with specific values
Related Documentation
- Evaluation Logic Reference - Operators and conditions using JSONPath
- Data Objects - Managing test data and input structures
- Rule Sets - Using JSONPath in rule conditions and actions
- Decision Tables - Column attribute paths and result storage
- Scorecards - Predictor attributes and output paths
- Decision Trees - Node conditions and action paths
Need Help? If you encounter issues with JSONPath attribute resolution, refer to the troubleshooting section above or check component-specific documentation for implementation examples.