Hvis du noen gang har stirret på et dypt nestet API-svar og tenkt "jeg trenger bare det ene feltet som er begravd der inne" — er JSONPath verktøyet du leter etter. Det er et spørrespråk for JSON, likt i ånd til hvordan XPath fungerer for XML. Du skriver et stiuttrykk, og du får de matchende verdiene tilbake. Ingen løkker, ingen manuell traversering. Stefan Goessner introduserte det i 2007, og etter år med litt inkompatible implementasjoner som sirkulerte rundt, ble det formelt standardisert som RFC 9535 i 2024. Du kan også utforske JSONPath-uttrykk interaktivt med JSON Path-verktøyet.
Datasettet vi vil bruke gjennom hele artikkelen
Fremfor abstrakte eksempler, la oss jobbe med noe realistisk — et e-handelsordresvar. Dette er den typen JSON du ville fått tilbake fra et ordrehåndterings-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
}
}
}Rotoperatoren $ og grunnleggende navigasjon
Hvert JSONPath-uttrykk starter med $, som refererer til dokumentets rot.
Derfra navigerer du med punktum. Vil du ha ordrestatus? Det er enkelt:
$.order.status
// → "shipped"
$.order.customer.name
// → "Maria Chen"
$.order.totals.total
// → 166.88
$.order.shippingAddress.city
// → "Portland"Du kan også bruke klammeparentesnotasjon, som er nyttig når en nøkkel inneholder mellomrom eller spesialtegn, eller når du aksesserer arrayelementer via indeks. Begge stiler er gyldige ifølge RFC 9535s syntaksregler:
// Punktnotasjon
$.order.customer.email
// Klammeparentesnotasjon — ekvivalent
$['order']['customer']['email']
// Arrayindeks — første linjepost
$.order.lineItems[0].name
// → "Wireless Headphones"
// Siste linjepost (negativ indeksering — støttet i RFC 9535)
$.order.lineItems[-1].name
// → "Phone Stand Pro"$[-1:] (slice-syntaks) og
$.array[-1] (direkte negativt indeks) er begge gyldige i RFC 9535, men noen eldre
implementasjoner støtter dem ikke. Test mot ditt spesifikke bibliotek hvis du bruker negative indekser.Jokertegn — spørr alle barn på én gang
Jokertegnet * matcher alle elementer på et gitt nivå. I stedet for å spørre om
lineItems[0], lineItems[1] osv., kan du hente alt på én gang:
// Alle linjepostnavn
$.order.lineItems[*].name
// → ["Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Alle linjeposters enhetspriser
$.order.lineItems[*].unitPrice
// → [89.99, 12.49, 34.00]
// Alle linjeposters SKU-koder
$.order.lineItems[*].sku
// → ["WH-1042", "CB-USB-C", "SC-PRO-7"]Dette er mønsteret du vil bruke oftest når du jobber med arrays i API-svar. I stedet for å mappe over en array i applikasjonskoden din, kan du trekke ut nøyaktig det feltet du trenger før det når forretningslogikken din.
Rekursivt søk — finn verdier på alle dybder
Operatoren .. gjør et rekursivt søk gjennom hele dokumenttreet.
Det er som en dybde-først-gjennomgang som samler alle noder som matcher nøkkelnavnet, uavhengig av hvor det befinner seg:
// Finn hvert "name"-felt et vilkårlig sted i dokumentet
$..name
// → ["Maria Chen", "Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Finn hvert "city"-felt et vilkårlig sted
$..city
// → ["Portland"]
// Finn hvert felt kalt "id" på alle dybder
$..id
// → ["ORD-9182", "CUST-441"]Legg merke til hvordan $..name finner både kundenavnet og alle produktnavnene —
det bryr seg ikke om dybden. Dette er kraftfullt for skjemautforskning: når du får et
ukjent JSON-klump og ønsker å finne alle verdier for en bestemt nøkkel uten å kjenne strukturen.
Hvis du vil sjekke selve JSON-filen først, er JSON Validator
og JSON Formatter praktiske startpunkter.
Array-slicing
JSONPath låner Python-stilens slice-notasjon for arrays. Formatet er
[start:end:step], der enhver del kan utelates.
Dette er dokumentert i RFC 9535 § 2.3.5:
// Første to linjepostene
$.order.lineItems[0:2]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Fra indeks 1 til slutten
$.order.lineItems[1:]
// → [{ sku: "CB-USB-C", ... }, { sku: "SC-PRO-7", ... }]
// Kun siste element (slice-syntaks — bredt støttet)
$.order.lineItems[-1:]
// → [{ sku: "SC-PRO-7", ... }]
// Hvert annet element (steg på 2)
$.order.lineItems[0::2]
// → [{ sku: "WH-1042", ... }, { sku: "SC-PRO-7", ... }]Filteruttrykk — spørring etter betingelse
Det er her JSONPath virkelig viser sin styrke. Filtersyntaksen [?(...)]
lar deg spørre array-elementer etter feltverdienes verdier. Symbolet @ refererer til det
gjeldende elementet som testes. Stefan Goessners
originale artikkel fra 2007
introduserte denne syntaksen, og RFC 9535 formaliserte den:
// Linjeposterne under $50
$.order.lineItems[?(@.unitPrice < 50)]
// → [{ sku: "CB-USB-C", unitPrice: 12.49, ... }, { sku: "SC-PRO-7", unitPrice: 34.00, ... }]
// Kun varer på lager
$.order.lineItems[?(@.inStock == true)]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Varer der qty er større enn 1
$.order.lineItems[?(@.qty > 1)]
// → [{ sku: "CB-USB-C", qty: 2, ... }]
// Varer som IKKE er på lager
$.order.lineItems[?(@.inStock == false)].name
// → ["Phone Stand Pro"]Du kan også kombinere betingelser. De fleste implementasjoner støtter && og ||
inne i filteruttrykk, noe som lar deg skrive ting som
[?(@.inStock == true && @.unitPrice < 30)].
Sjekk bibliotekets dokumentasjon — atferd rundt operatørstøtte var en av de viktigste inkonsekvensene
mellom pre-RFC-implementasjoner som
RFC 9535-standardiseringen hadde som mål å fikse.
[?(@.fieldName)]. Hvis for eksempel
noen linjepostre hadde et discountCode-felt og andre ikke,
ville $.order.lineItems[?(@.discountCode)] returnere bare de rabatterte.Bruke JSONPath i JavaScript
Det er ingen innebygd JSONPath-støtte i JavaScript ennå (i motsetning til XPath, som har
document.evaluate() i nettleseren). Du trenger et bibliotek.
De to mest populære er jsonpath
og jsonpath-plus.
Pakken jsonpath-plus vedlikeholdes mer aktivt og har bedre RFC 9535-tilpasning:
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 at JSONPath() alltid returnerer en array — selv når du spør etter én enkelt verdi.
Det er med vilje: et stiuttrykk er en selektor, og en selektor kan matche null, én eller mange noder.
Så ta alltid result[0] når du vet at du sikter på en unik verdi.
Bruke JSONPath i Python
I Python er biblioteket
jsonpath-ng
det mest komplette alternativet. Det støtter kjernespesifikasjonen pluss det meste av filteruttrykkssyntaksen:
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 STOCKMønsteret parse() + find() er riktig tilnærming for produksjonsbruk —
kompiler uttrykket én gang og kjør det mot mange dokumenter, fremfor å re-parse stistrengen hver gang.
jsonpath-ng-dokumentasjonen på GitHub
har mer detalj om utvidet filtersyntaks og metoden update() for å endre matchede noder.
JSONPath vs jq — hvilken bør du bruke?
Folk forveksler noen ganger JSONPath og jq — begge "spør JSON", men de løser forskjellige problemer. Her er den praktiske fordelingen:
- JSONPath er et stiuttrykksspråk designet for å bygges inn i applikasjoner. Det spør og trekker ut verdier. Du bruker det inne i JavaScript-, Python- eller Java-kode, eller konfigurasjonsfiler som Kubernetes-selektorer.
- jq er en fullstendig kommandolinjeprosessor med sitt eget programmeringsspråk. Det kan spørre, transformere, omforme, filtrere og beregne nye verdier. Det er det riktige verktøyet for shell-skript og engangs-databehandling i en terminal.
- Bruk JSONPath når du skriver applikasjonskode som programmatisk trenger å trekke ut felt fra JSON — spesielt når biblioteket trenger å være innleggbart og lettvektig.
- Bruk jq når du er på kommandolinjen, skriver shell-skript, debugger API-svar, eller trenger å gjøre transformasjoner (omdøping av felt, aggregering, bygging av nye strukturer) som går utover enkel uttrekking.
- jq-syntaks er mer kraftfull men også mer kompleks —
jq '.order.lineItems[] | select(.inStock) | .name'vs JSONPath:s$.order.lineItems[?(@.inStock)].name. For ren uttrekking er JSONPath ofte mer lesbar.
Det er også et midtterreng: hvis du allerede jobber i JavaScript og bare trenger av og til
transformasjoner, dekker noe som Lodash:s _.get()
enkel stiaksess uten noen avhengigheter. Men for alt som involverer jokertegn, rekursjon
eller filteruttrykk er et skikkelig JSONPath-bibliotek verdt det.
Oppsummering
JSONPath fyller et reelt gap mellom "traverser manuelt dette objektet i en for-løkke" og
"start en jq-prosess". Når du er komfortabel med $, ., [*],
.. og [?(filter)], vil du gripe etter det konstant —
spesielt når du jobber med store API-nyttelaster der du bare trenger en håndfull felt.
RFC 9535-standardiseringen
betyr at syntaksen nå er stabil, så uttrykk du skriver i dag bør fungere konsekvent på tvers av
kompatible biblioteker.
Prøv uttrykkene dine med JSON Path-verktøyet — lim inn JSON-en din,
skriv en sti og se resultatene øyeblikkelig. Og hvis JSON-en din trenger å ryddes opp først,
er JSON Formatter og JSON Validator
rett der også.