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:

json
{
  "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:

text
$.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:

text
// 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"
Let op negatieve indexering: $[-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:

text
// 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:

text
// 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:

text
// 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:

text
// 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.

Controleren op het bestaan van een veld: Om items te filteren die een bepaald veld hebben (ongeacht de waarde), gebruik je de existentiecontrolesyntaxis: [?(@.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:

bash
npm install jsonpath-plus
js
import { 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.88

Merk 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:

bash
pip install jsonpath-ng
python
from 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 STOCK

Het 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.