Als je ooit naar een diep geneste API-response hebt gestaard en dacht "ik heb alleen dat ene veld nodig dat daar begraven zit" — JSONPath is het gereedschap dat je zoekt. Het is een querytaal voor JSON, vergelijkbaar in opzet met hoe XPath werkt voor XML. Je schrijft een padexpressie, je krijgt de overeenkomende waarden terug. Geen lussen, geen handmatige traversal. Stefan Goessner introduceerde het in 2007, en na jaren van licht incompatibele implementaties werd het formeel gestandaardiseerd als RFC 9535 in 2024. Je kunt JSONPath-expressies ook interactief verkennen met de JSON Path tool.
De Dataset die We Gebruiken
In plaats van abstracte voorbeelden werken we met iets realistisch — een e-commerce bestelresponse. Dit is het soort JSON dat je terugkrijgt van een orderbeheer-API:
{
"order": {
"id": "ORD-9182",
"status": "shipped",
"customer": {
"id": "CUST-441",
"name": "Maria Chen",
"email": "[email protected]"
},
"shippingAddress": {
"street": "14 Maple Avenue",
"city": "Portland",
"state": "OR",
"zip": "97201",
"country": "US"
},
"lineItems": [
{
"sku": "WH-1042",
"name": "Wireless Headphones",
"qty": 1,
"unitPrice": 89.99,
"inStock": true
},
{
"sku": "CB-USB-C",
"name": "USB-C Charging Cable",
"qty": 2,
"unitPrice": 12.49,
"inStock": true
},
{
"sku": "SC-PRO-7",
"name": "Phone Stand Pro",
"qty": 1,
"unitPrice": 34.00,
"inStock": false
}
],
"totals": {
"subtotal": 148.97,
"shipping": 5.99,
"tax": 11.92,
"total": 166.88
}
}
}De Root-operator $ en Basisnavigatie
Elke JSONPath-expressie begint met $, dat verwijst naar de root van het document.
Vanaf daar navigeer je met punten. De bestellingsstatus opvragen? Dat is eenvoudig:
$.order.status
// → "shipped"
$.order.customer.name
// → "Maria Chen"
$.order.totals.total
// → 166.88
$.order.shippingAddress.city
// → "Portland"Je kunt ook haakjesnotatie gebruiken, wat handig is als een sleutel spaties of speciale tekens bevat, of wanneer je array-elementen via index benadert. Beide stijlen zijn geldig volgens de syntaxregels van RFC 9535:
// Dot notation
$.order.customer.email
// Bracket notation — equivalent
$['order']['customer']['email']
// Array index — first line item
$.order.lineItems[0].name
// → "Wireless Headphones"
// Last line item (negative indexing — supported in RFC 9535)
$.order.lineItems[-1].name
// → "Phone Stand Pro"$[-1:] (slice-syntaxis) en
$.array[-1] (directe negatieve index) zijn beide geldig in RFC 9535, maar sommige oudere
implementaties ondersteunen ze niet. Test met je specifieke bibliotheek als je negatieve indexen gebruikt.Wildcards — Alle Kinderen in Één Keer Opvragen
De * wildcard matcht alle elementen op een gegeven niveau. In plaats van
lineItems[0], lineItems[1], enz. op te vragen, kun je alles in één keer pakken:
// All line item names
$.order.lineItems[*].name
// → ["Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// All line item unit prices
$.order.lineItems[*].unitPrice
// → [89.99, 12.49, 34.00]
// All line item SKUs
$.order.lineItems[*].sku
// → ["WH-1042", "CB-USB-C", "SC-PRO-7"]Dit is het patroon dat je het meest gebruikt bij het werken met arrays in API-responses. In plaats van in je applicatiecode over een array te mappen, kun je precies het veld extraheren dat je nodig hebt voordat het je bedrijfslogica bereikt.
Recursieve Afdaling — Waarden op Elke Diepte Vinden
De .. operator voert een recursieve zoekopdracht uit door de hele documentboom.
Het is als een diepte-eerst wandeling die elk knooppunt verzamelt dat overeenkomt met de sleutelnaam, ongeacht waar het zich bevindt:
// Find every "name" field anywhere in the document
$..name
// → ["Maria Chen", "Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Find every "city" field anywhere
$..city
// → ["Portland"]
// Find every field called "id" at any depth
$..id
// → ["ORD-9182", "CUST-441"]Merk op hoe $..name zowel de klantnaam als alle productnamen vindt —
het geeft niet om de diepte. Dit is krachtig voor schemaverkenning: wanneer je een onbekende JSON-blob
krijgt en alle waarden voor een bepaalde sleutel wilt vinden zonder de structuur te kennen.
Als je eerst de JSON zelf wilt controleren, zijn de JSON Validator
en JSON Formatter handige startpunten.
Array Slicing
JSONPath leent Python-stijl slicenotatie voor arrays. Het formaat is
[start:end:step], waarbij elk onderdeel weggelaten kan worden.
Dit is gedocumenteerd in RFC 9535 § 2.3.5:
// First two line items
$.order.lineItems[0:2]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// From index 1 to end
$.order.lineItems[1:]
// → [{ sku: "CB-USB-C", ... }, { sku: "SC-PRO-7", ... }]
// Last item only (slice syntax — widely supported)
$.order.lineItems[-1:]
// → [{ sku: "SC-PRO-7", ... }]
// Every other item (step of 2)
$.order.lineItems[0::2]
// → [{ sku: "WH-1042", ... }, { sku: "SC-PRO-7", ... }]Filterexpressies — Opvragen op Basis van Voorwaarden
Hier verdient JSONPath zijn geld echt terug. De filtersyntaxis [?(...)]
laat je array-items opvragen op basis van hun veldwaarden. Het @ symbool verwijst naar het huidige
item dat getest wordt. Het
originele artikel van Stefan Goessner uit 2007
introduceerde deze syntaxis, en RFC 9535 formaliseerde die:
// Line items under $50
$.order.lineItems[?(@.unitPrice < 50)]
// → [{ sku: "CB-USB-C", unitPrice: 12.49, ... }, { sku: "SC-PRO-7", unitPrice: 34.00, ... }]
// Only items currently in stock
$.order.lineItems[?(@.inStock == true)]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Items where qty is greater than 1
$.order.lineItems[?(@.qty > 1)]
// → [{ sku: "CB-USB-C", qty: 2, ... }]
// Items that are NOT in stock
$.order.lineItems[?(@.inStock == false)].name
// → ["Phone Stand Pro"]Je kunt voorwaarden ook combineren. De meeste implementaties ondersteunen && en ||
binnen filterexpressies, waardoor je dingen kunt schrijven als
[?(@.inStock == true && @.unitPrice < 30)].
Controleer de documentatie van je bibliotheek — het gedrag rond operatorondersteuning was een van de belangrijkste inconsistenties
tussen pre-RFC-implementaties die de
RFC 9535-standaardisatie wilde oplossen.
[?(@.fieldName)]. Als sommige regelitems
bijvoorbeeld een discountCode-veld hadden en andere niet,
zou $.order.lineItems[?(@.discountCode)] alleen de kortingsitems teruggeven.JSONPath Gebruiken in JavaScript
Er is nog geen ingebouwde JSONPath-ondersteuning in JavaScript (in tegenstelling tot XPath, dat
document.evaluate() in de browser heeft). Je hebt een bibliotheek nodig.
De twee populairste zijn jsonpath
en jsonpath-plus.
Het pakket jsonpath-plus wordt actiever onderhouden en heeft betere RFC 9535-afstemming:
npm install jsonpath-plusimport { JSONPath } from 'jsonpath-plus';
const order = {
order: {
id: 'ORD-9182',
status: 'shipped',
customer: { name: 'Maria Chen', email: '[email protected]' },
lineItems: [
{ sku: 'WH-1042', name: 'Wireless Headphones', qty: 1, unitPrice: 89.99, inStock: true },
{ sku: 'CB-USB-C', name: 'USB-C Charging Cable', qty: 2, unitPrice: 12.49, inStock: true },
{ sku: 'SC-PRO-7', name: 'Phone Stand Pro', qty: 1, unitPrice: 34.00, inStock: false }
],
totals: { subtotal: 148.97, shipping: 5.99, tax: 11.92, total: 166.88 }
}
};
// Get all product names
const names = JSONPath({ path: '$.order.lineItems[*].name', json: order });
console.log(names);
// [ 'Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro' ]
// Get in-stock items under $50
const affordable = JSONPath({ path: '$.order.lineItems[?(@.inStock == true && @.unitPrice < 50)]', json: order });
console.log(affordable.map(item => item.name));
// [ 'USB-C Charging Cable' ]
// Get the order total
const total = JSONPath({ path: '$.order.totals.total', json: order });
console.log(total[0]); // 166.88Merk op dat JSONPath() altijd een array retourneert — zelfs wanneer je een enkelvoudige waarde opvraagt.
Dat is by design: een padexpressie is een selector, en een selector kan nul, één of vele knooppunten matchen.
Pak dus altijd result[0] wanneer je weet dat je een unieke waarde target.
JSONPath Gebruiken in Python
In Python is de
jsonpath-ng
bibliotheek de meest complete optie. Het ondersteunt de kernspecificatie plus de meeste filtersyntaxis:
pip install jsonpath-ngfrom jsonpath_ng import parse
order = {
"order": {
"id": "ORD-9182",
"status": "shipped",
"customer": {"name": "Maria Chen", "email": "[email protected]"},
"lineItems": [
{"sku": "WH-1042", "name": "Wireless Headphones", "qty": 1, "unitPrice": 89.99, "inStock": True},
{"sku": "CB-USB-C", "name": "USB-C Charging Cable", "qty": 2, "unitPrice": 12.49, "inStock": True},
{"sku": "SC-PRO-7", "name": "Phone Stand Pro", "qty": 1, "unitPrice": 34.00, "inStock": False}
],
"totals": {"subtotal": 148.97, "shipping": 5.99, "tax": 11.92, "total": 166.88}
}
}
# Parse the expression once, then reuse it (more efficient)
expr = parse("$.order.lineItems[*].name")
names = [match.value for match in expr.find(order)]
print(names)
# ['Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro']
# Get all line items and check stock
expr2 = parse("$.order.lineItems[*]")
for item in [m.value for m in expr2.find(order)]:
status = "in stock" if item["inStock"] else "OUT OF STOCK"
print(f"{item['name']} (${item['unitPrice']}) — {status}")
# Output:
# Wireless Headphones ($89.99) — in stock
# USB-C Charging Cable ($12.49) — in stock
# Phone Stand Pro ($34.0) — OUT OF STOCKHet parse() + find() patroon is de juiste aanpak voor productiegebruik —
compileer de expressie eenmalig en voer die uit op veel documenten, in plaats van de padstring elke keer opnieuw te parsen.
De jsonpath-ng documentatie op GitHub
heeft meer details over uitgebreide filtersyntaxis en de update()-methode voor het wijzigen van gematchte knooppunten.
JSONPath vs jq — Welke Kies Je?
Mensen verwarren soms JSONPath en jq — ze "bevragen beide JSON", maar ze lossen verschillende problemen op. Dit is de praktische vergelijking:
- JSONPath is een padexpressietaal die is ontworpen om in applicaties te worden ingebed. Het bevraagt en extraheert waarden. Je gebruikt het binnen JavaScript, Python, Java-code of configuratiebestanden zoals Kubernetes-selectors.
- jq is een volledige opdrachtregelprocessor met zijn eigen programmeertaal. Het kan bevragen, transformeren, hervormen, filteren en nieuwe waarden berekenen. Het is het juiste gereedschap voor shellscripts en eenmalige datamanipulatie in een terminal.
- Gebruik JSONPath wanneer je applicatiecode schrijft die programmatisch velden uit JSON moet extraheren — vooral wanneer de bibliotheek inbedbaar en lichtgewicht moet zijn.
- Gebruik jq wanneer je op de opdrachtregel werkt, shellscripts schrijft, API-responses debugt, of transformaties moet uitvoeren (velden hernoemen, aggregeren, nieuwe structuren bouwen) die verder gaan dan eenvoudige extractie.
- jq-syntaxis is krachtiger maar ook complexer —
jq '.order.lineItems[] | select(.inStock) | .name'vs JSONPath's$.order.lineItems[?(@.inStock)].name. Voor pure extractie is JSONPath vaak leesbaarder.
Er is ook een middenweg: als je al in JavaScript werkt en slechts af en toe transformaties nodig hebt,
dekt iets als Lodash's _.get()
eenvoudige padtoegang zonder afhankelijkheden. Maar voor alles met wildcards, recursie
of filterexpressies is een goede JSONPath-bibliotheek het waard.
Samenvatting
JSONPath vult een echte leemte tussen "handmatig dit object traverseren in een for-lus" en
"een jq-proces opstarten". Zodra je vertrouwd bent met $, ., [*],
.. en [?(filter)], zul je er constant naar grijpen —
vooral bij grote API-payloads waar je slechts een handvol velden nodig hebt.
De RFC 9535-standaardisatie
betekent dat de syntaxis nu stabiel is, zodat expressies die je vandaag schrijft consistent moeten werken over
conforme bibliotheken.
Probeer je expressies uit met de JSON Path tool — plak je JSON,
schrijf een pad en bekijk de resultaten direct. En als je JSON eerst moet worden opgeschoond,
zijn de JSON Formatter en JSON Validator
ook beschikbaar.