Tutorial · 6 min read
How to Use JSONPath Wildcards and Filters with Examples
Index-based paths break when array lengths change. Wildcards, filters, and recursive descent give you queries that adapt. Use a JSONPath wildcard tester to experiment with every expression in this guide.
1. Wildcard (*): Select Without Knowing Indices
The wildcard * selects all elements at the current level — whether it's an array or an object. This is the foundation of flexible JSONPath queries:
// Sample JSON
{
"employees": [
{ "name": "Alice", "dept": "Engineering" },
{ "name": "Bob", "dept": "Design" },
{ "name": "Carol", "dept": "Engineering" }
],
"office": { "city": "New York", "floors": 10 }
}
// Query: all employees
$.employees[*]
// Result: [Alice, Bob, Carol]
// Query: all values in the root object
$.employees, $.office
// Query: all values in office
$.office.*
// Result: ["New York", 10]
Use [*] for arrays and .* for objects. Many implementations treat them the same, but distinguishing them makes intent clearer. If you need a refresher on basic JSONPath syntax, check the JSONPath basic tutorial first.
The JSONPath wildcard tester lets you paste any JSON and test $.* or $[*] instantly.
2. Recursive Descent (..): Find Fields at Any Depth
The double-dot .. operator searches the entire JSON tree, no matter how deeply a field is nested:
// Sample deeply nested JSON
{
"deep": {
"deeper": {
"target": "found me!",
"array": [
{ "target": "also found" }
]
},
"sibling": { "target": "and me!" }
}
}
// Query: find ALL "target" values at any depth
$..target
// Result: ["found me!", "also found", "and me!"]
// Query: find all "target" inside 'deeper' subtree
$.deep.deeper..target
// Result: ["found me!", "also found"]
Performance note: .. visits every node in the JSON tree. On large files, this can be slow. Use it sparingly and prefer targeted paths when you know the structure.
You can combine recursive descent with other operators: $..[?(@.type === 'error')] finds all objects with a type of "error" anywhere in the document.
3. Filter Expressions [?()]: Select by Condition
Filters are the most powerful JSONPath feature. They let you select array elements based on conditions using @ (current element):
// Products data
{
"products": [
{ "name": "Widget A", "price": 25, "stock": 100 },
{ "name": "Widget B", "price": 50, "stock": 0 },
{ "name": "Widget C", "price": 75, "stock": 10 }
]
}
// Filter: find products with price > 30
$.products[?(@.price > 30)]
// Result: [Widget B, Widget C]
// Filter: find products in stock
$.products[?(@.stock > 0)]
// Result: [Widget A, Widget C]
// Filter: exact string match
$.products[?(@.name == "Widget A")]
// Result: [Widget A]
// Filter: using .length on strings
$.products[?(@.name.length > 7)]
// Result: [Widget B, Widget C]
Comparison operators work with numbers, strings, and booleans. Use == for equality, != for not-equal, and <, <=, >, >= for numeric comparison.
4. Multi-Condition Filters with AND/OR
Combine conditions with && (AND) and || (OR). Parentheses control evaluation order:
// More products
{
"products": [
{ "name": "A", "price": 25, "stock": 100, "category": "tool" },
{ "name": "B", "price": 50, "stock": 0, "category": "tool" },
{ "name": "C", "price": 75, "stock": 10, "category": "food" },
{ "name": "D", "price": 100, "stock": 5, "category": "food" }
]
}
// AND: in stock AND category is "tool"
$.products[?(@.stock > 0 && @.category == "tool")]
// Result: [A]
// OR: price > 60 OR out of stock
$.products[?(@.price > 60 || @.stock == 0)]
// Result: [B, C, D]
// Grouped: (in stock AND cheap) OR category tool
$.products[?((@.stock > 0 && @.price < 30) || @.category == "tool")]
// Result: [A, B]
Most JSONPath implementations support these logical operators, but always test complex expressions in a JSONPath tester to confirm behavior matches your expectations.
5. Filtering on Nested Object Properties
You can filter based on nested properties by chaining dot notation inside the filter:
// Users with nested address
{
"users": [
{
"name": "Alice",
"address": { "city": "New York", "zip": "10001" },
"scores": [85, 92, 78]
},
{
"name": "Bob",
"address": { "city": "Boston", "zip": "02101" },
"scores": [70, 65, 80]
}
]
}
// Filter: users in New York
$.users[?(@.address.city == "New York")]
// Result: [Alice]
// Filter: users with any score >= 90
$.users[?(@.scores[0] >= 90 || @.scores[1] >= 90 || @.scores[2] >= 90)]
// Result: [Alice]
// Filter: users with length of scores > 2
$.users[?(@.scores.length > 2)]
// Result: [Alice, Bob]
Nested dot notation works inside filter expressions the same way it works in paths. You can access array elements by index (@.scores[0]) and object properties by key (@.address.city).
6. Combining Wildcard with Filters
Wildcards and filters work together. The wildcard selects all elements, and the filter narrows them down:
// Mixed structure
{
"data": {
"users": [
{ "type": "premium", "name": "Alice" },
{ "type": "free", "name": "Bob" }
],
"admins": [
{ "type": "premium", "name": "Carol" }
]
}
}
// Find premium users anywhere under data
$.data.*[?(@.type == "premium")]
// Result: [Alice, Carol]
This pattern is especially useful when your JSON has multiple arrays that share the same structure — one wildcard query covers all of them.
Try the JSONPath Tester
Paste any JSON and test wildcards, filters, and recursive descent expressions in real-time. See matched results instantly.
Test JSONPath Now →Best Practices for JSONPath Wildcards and Filters
- Prefer explicit paths over .. Recursive descent is powerful but expensive. If you know the depth, write the full path.
- Test filter expressions in browser first. Different JSONPath implementations (Goessner, Jayway, Python's jsonpath-ng) have subtle differences. Always test in the JSONPath tester before writing code.
- Use parentheses for complex conditions. Without parentheses,
&&and||precedence can produce unexpected results. Always group explicitly. - Start with a basic tutorial. If you're new to JSONPath, work through the basic JSONPath tutorial before tackling advanced expressions.
- Consider null safety. Filtering on a property that might not exist can cause errors. Some implementations support
?(@.price < 10 && @.price != null)to safely check existence.
Frequently Asked Questions
What is the difference between [*] and .* in JSONPath?
[*] selects all elements of an array, while .* (or ['*']) selects all values of an object. In practice, many implementations treat them interchangeably. Use [*] for arrays and .* for objects for clarity.
Does JSONPath filter support regular expressions?
JSONPath itself does not define regex support, but some implementations (like Goessner's) add it via the ~= operator (e.g., $..[?(@.name ~= /^A.*/)]). Java's Jayway implementation supports the match() function. Check your library's documentation.
Does recursive descent (..) impact performance on large JSON?
Yes. Recursive descent traverses every node in the entire JSON tree. On large files (10,000+ nodes), .. expressions can be significantly slower than explicit paths. Use .. sparingly and prefer targeted paths when performance matters.
Can I use AND/OR in a single JSONPath filter expression?
Yes. Most implementations support logical operators within filter expressions: && for AND, || for OR. Example: $[?(@.price > 10 && @.stock > 0)]. You can also nest filters with parentheses for complex conditions.
Can I export JSONPath query results to CSV?
Some JSONPath testers include a CSV export feature. If not, copy the results and paste them into a JSON to CSV converter, or use a JSONPath library in your programming language to process results programmatically.
Looking for more guides? See the full JSONXX How To index.