QuivaWorks uses JSONPath Plus v1.1.0 which provides powerful operators for querying and selecting data from complex structures.
These are standard JSONPath features that work across all JSONPath implementations. The next sections cover QuivaWorks-specific extensions.
Wildcard Selection
Select all elements at a specific level using the wildcard operator [*].
Array Wildcard
Object Wildcard
Nested Wildcard
{
"fetch_orders" : {
"orders" : [
{ "id" : "ORD-001" , "total" : 99.99 , "status" : "shipped" },
{ "id" : "ORD-002" , "total" : 149.99 , "status" : "pending" },
{ "id" : "ORD-003" , "total" : 79.99 , "status" : "delivered" }
]
}
}
{
"allOrderIds" : "$.fetch_orders.orders[*].id" ,
"allTotals" : "$.fetch_orders.orders[*].total" ,
"allStatuses" : "$.fetch_orders.orders[*].status"
}
{
"allOrderIds" : [ "ORD-001" , "ORD-002" , "ORD-003" ],
"allTotals" : [ 99.99 , 149.99 , 79.99 ],
"allStatuses" : [ "shipped" , "pending" , "delivered" ]
}
{
"api_response" : {
"users" : {
"user1" : { "name" : "Alice" , "age" : 30 },
"user2" : { "name" : "Bob" , "age" : 25 },
"user3" : { "name" : "Charlie" , "age" : 35 }
}
}
}
{
"allUsers" : "$.api_response.users.*" ,
"allNames" : "$.api_response.users.*.name" ,
"allAges" : "$.api_response.users.*.age"
}
{
"allUsers" : [
{ "name" : "Alice" , "age" : 30 },
{ "name" : "Bob" , "age" : 25 },
{ "name" : "Charlie" , "age" : 35 }
],
"allNames" : [ "Alice" , "Bob" , "Charlie" ],
"allAges" : [ 30 , 25 , 35 ]
}
{
"departments" : [
{
"name" : "Sales" ,
"employees" : [
{ "name" : "Alice" , "role" : "Manager" },
{ "name" : "Bob" , "role" : "Rep" }
]
},
{
"name" : "Engineering" ,
"employees" : [
{ "name" : "Charlie" , "role" : "Developer" },
{ "name" : "Diana" , "role" : "Designer" }
]
}
]
}
{
"allEmployeeNames" : "$.node.departments[*].employees[*].name"
}
{
"allEmployeeNames" : [ "Alice" , "Bob" , "Charlie" , "Diana" ]
}
Wildcard [*] always returns an array , even if there’s only one item. If you need a single value, use an index like [0] instead.
Recursive Descent
Search for properties at any depth in the structure using .. (double dot).
Find All Occurrences
Deep Search
{
"allEmails" : "$.fetch_data..email"
}
Finds all email properties regardless of nesting level. {
"company" : {
"departments" : [
{
"name" : "Sales" ,
"manager" : { "name" : "Alice" },
"teams" : [
{
"name" : "Team A" ,
"lead" : { "name" : "Bob" }
}
]
},
{
"name" : "Engineering" ,
"manager" : { "name" : "Charlie" }
}
]
}
}
{
"allPersonNames" : "$.node..name"
}
{
"allPersonNames" : [ "Sales" , "Alice" , "Team A" , "Bob" , "Engineering" , "Charlie" ]
}
Note: Returns ALL properties named “name”, including department and team names.
When to use recursive descent:
Structure varies and you need to find all occurrences
You don’t know the exact depth of the property
You’re searching across multiple nested levels
When NOT to use:
You know the exact path (use direct path for better performance)
You only want a specific occurrence (use explicit path)
Array Slicing
Extract portions of arrays using slice notation [start:end:step].
Basic Slicing
Negative Indexes
Step Values
{
"items" : [
{ "id" : 1 , "name" : "Item 1" },
{ "id" : 2 , "name" : "Item 2" },
{ "id" : 3 , "name" : "Item 3" },
{ "id" : 4 , "name" : "Item 4" },
{ "id" : 5 , "name" : "Item 5" }
]
}
{
"firstThree" : "$.node.items[0:3]" ,
"middleTwo" : "$.node.items[1:3]" ,
"lastTwo" : "$.node.items[3:5]" ,
"fromThirdOnward" : "$.node.items[2:]" ,
"upToThird" : "$.node.items[:3]"
}
{
"firstThree" : [ Item 1 , Item 2 , Item 3 ],
"middleTwo" : [ Item 2 , Item 3 ],
"lastTwo" : [ Item 4 , Item 5 ],
"fromThirdOnward" : [ Item 3 , Item 4 , Item 5 ],
"upToThird" : [ Item 1 , Item 2 , Item 3 ]
}
{
"items" : [ "A" , "B" , "C" , "D" , "E" , "F" ]
}
{
"lastThree" : "$.node.items[-3:]" ,
"allButLast" : "$.node.items[:-1]" ,
"secondToLast" : "$.node.items[-2:-1]" ,
"lastItem" : "$.node.items[-1]"
}
{
"lastThree" : [ "D" , "E" , "F" ],
"allButLast" : [ "A" , "B" , "C" , "D" , "E" ],
"secondToLast" : [ "E" ],
"lastItem" : "F"
}
{
"numbers" : [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
}
{
"everyOther" : "$.node.numbers[::2]" ,
"everyThird" : "$.node.numbers[::3]" ,
"evenIndexes" : "$.node.numbers[0::2]" ,
"oddIndexes" : "$.node.numbers[1::2]"
}
{
"everyOther" : [ 1 , 3 , 5 , 7 , 9 ],
"everyThird" : [ 1 , 4 , 7 , 10 ],
"evenIndexes" : [ 1 , 3 , 5 , 7 , 9 ],
"oddIndexes" : [ 2 , 4 , 6 , 8 , 10 ]
}
Slice Syntax
start - Index to begin at (inclusive, default: 0)
end - Index to stop at (exclusive, default: array length)
step - Increment between items (default: 1)
Parent Selector
Get the parent object of a matched item using the ^ operator.
Basic Parent Access
Filter Then Get Parent
{
"products" : [
{
"name" : "Laptop" ,
"price" : 999 ,
"specs" : { "cpu" : "i7" , "ram" : 16 }
},
{
"name" : "Mouse" ,
"price" : 25 ,
"specs" : { "wireless" : true }
}
]
}
{
"expensiveProduct" : "$.node.products[?(@.price>100)]^"
}
The ^ returns the parent array containing the expensive items. {
"store" : {
"inventory" : [
{ "item" : "Widget" , "stock" : 50 },
{ "item" : "Gadget" , "stock" : 5 },
{ "item" : "Doohickey" , "stock" : 100 }
]
}
}
{
"lowStockParent" : "$.node.store.inventory[?(@.stock<10)]^"
}
First filters items with low stock, then ^ returns the parent inventory array.
The parent selector is useful when you need to reference the container of filtered items, not just the items themselves.
Property Name Selector
Get property names instead of values using the ~ operator.
Object Property Names
Array Index Names
Nested Property Names
{
"api_response" : {
"user" : { "name" : "Alice" },
"account" : { "balance" : 1000 },
"settings" : { "theme" : "dark" }
}
}
{
"propertyNames" : "$.node.api_response.*~"
}
{
"propertyNames" : [ "user" , "account" , "settings" ]
}
{
"items" : [ "Apple" , "Banana" , "Cherry" ]
}
{
"indexNames" : "$.node.items[*]~"
}
{
"indexNames" : [ "0" , "1" , "2" ]
}
For arrays, ~ returns string representations of indexes. {
"config" : {
"database" : { "host" : "localhost" },
"cache" : { "ttl" : 3600 },
"api" : { "timeout" : 30 }
}
}
{
"configSections" : "$.node.config.*~" ,
"databaseSettings" : "$.node.config.database.*~"
}
{
"configSections" : [ "database" , "cache" , "api" ],
"databaseSettings" : [ "host" ]
}
Bracket Notation
With JSON path, you can access properties with special characters using bracket notation ['property']. QuivaWorks allows you to access properties with special characters without using this notation, however if you are experiencing unexpected results you can still use this notation.
Spaces in Names
Special Characters
Numeric String Keys
{
"api response" : {
"user data" : {
"first name" : "Alice"
}
}
}
{
"userName" : "$['api response']['user data']['first name']"
}
{
"response" : {
"property-with-dashes" : "value1" ,
"property.with.dots" : "value2" ,
"property@special" : "value3"
}
}
{
"value1" : "$.node.response['property-with-dashes']" ,
"value2" : "$.node.response['property.with.dots']" ,
"value3" : "$.node.response['property@special']"
}
{
"data" : {
"123" : "numeric string key" ,
"456" : "another numeric key"
}
}
{
"first" : "$.node.data['123']" ,
"second" : "$.node.data['456']"
}
When to use bracket notation:
Property names contain spaces
Property names contain special characters (. - @ # $ etc.)
Property names are numeric strings
Property names could be confused with JSONPath operators
Escaping Special Characters
Use backticks to escape property names that might conflict with JSONPath operators.
Dollar Sign Property
At Sign Property
Literal Backtick
{
"$price" : 100 ,
"$total" : 500
}
{
"price" : "$.node.`$price`" ,
"total" : "$.node.`$total`"
}
Without backticks, $ would be interpreted as the root operator. {
"@timestamp" : "2025-01-15T10:30:00Z" ,
"@version" : "2.0"
}
{
"timestamp" : "$.node.`@timestamp`" ,
"version" : "$.node.`@version`"
}
Without backticks, @ would be interpreted as a filter variable. {
"value" : "$.node.``code``"
}
Use double backticks to escape a literal backtick character.
Important Notes
JSONPath uses 0-based indexing like JavaScript (not 1-based like XPath):
First element: [0]
Second element: [1]
Last element: [-1]
Second to last: [-2]
All JSONPath expressions are case-sensitive :
$.user.Name ≠ $.user.name
$.NODE_ID ≠ $.node_id
Property names must match exactly
Understanding what JSONPath returns:
Single property : Returns the value directly
Wildcard [*] : Always returns an array
Filter [?()] : Always returns an array
Recursive .. : Always returns an array
Slice [:] : Always returns an array
Non-existent path : Property is removed from output entirely
Common Patterns
Get All Values
Slice Arrays
Get Structure
{
"allEmails" : "$.users[*].email" ,
"allPrices" : "$.products[*].price" ,
"allIds" : "$..id"
}
{
"topThree" : "$.leaderboard[0:3]" ,
"recentFive" : "$.logs[-5:]" ,
"everyOther" : "$.items[::2]"
}
{
"sections" : "$.config.*~" ,
"firstLevelKeys" : "$.*~" ,
"arrayIndexes" : "$.items[*]~"
}
Try It Yourself
Create Sample Data
Add a node that returns complex nested JSON data
Use Wildcards
Try [*] to select all items in an array
Try Recursive Search
Use ..propertyName to find all occurrences
Experiment with Slicing
Practice array slicing with different start:end:step combinations
Test in Debugger
Verify results in the Flow Debugger
What’s Next?