Om du någonsin stirrat på ett djupt nästlat API-svar och tänkt "jag behöver bara det där ena fältet som är begravt därinne" — JSONPath är verktyget du letar efter. Det är ett frågespråk för JSON, liknande till sin natur hur XPath fungerar för XML. Du skriver ett sökvägsuttryck och får tillbaka de matchande värdena. Inga loopar, ingen manuell traversering. Stefan Goessner introducerade det 2007, och efter år av något inkompatibla implementationer som cirkulerade runt, standardiserades det formellt som RFC 9535 år 2024. Du kan också utforska JSONPath-uttryck interaktivt med JSON Path-verktyget.
Datamängden vi kommer att använda genomgående
Istället för abstrakta exempel, låt oss arbeta med något realistiskt — ett e-handelssvar för en order. Det här är den typ av JSON du skulle få tillbaka från ett API för orderhantering:
{
"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
}
}
}Rotoperatorn $ och grundläggande navigering
Varje JSONPath-uttryck börjar med $, som refererar till dokumentets rot.
Därifrån navigerar du med punkter. Vill du ha orderstatus? Det är enkelt:
$.order.status
// → "shipped"
$.order.customer.name
// → "Maria Chen"
$.order.totals.total
// → 166.88
$.order.shippingAddress.city
// → "Portland"Du kan också använda hakparentesnotation, vilket är användbart när en nyckel innehåller mellanslag eller specialtecken, eller när du ska komma åt arrayelement via index. Båda stilarna är giltiga enligt RFC 9535:s syntaxregler:
// Punktnotation
$.order.customer.email
// Hakparentesnotation — ekvivalent
$['order']['customer']['email']
// Arrayindex — första radartikel
$.order.lineItems[0].name
// → "Wireless Headphones"
// Sista radartikel (negativt indexering — stöds i RFC 9535)
$.order.lineItems[-1].name
// → "Phone Stand Pro"$[-1:] (slice-syntax) och
$.array[-1] (direkt negativt index) är båda giltiga i RFC 9535, men en del äldre
implementationer stöder dem inte. Testa mot ditt specifika bibliotek om du använder negativa index.Jokertecken — fråga alla barn på en gång
Jokertecknet * matchar alla element på en given nivå. Istället för att fråga om
lineItems[0], lineItems[1] osv., kan du ta allt på en gång:
// Alla radartikelnnamn
$.order.lineItems[*].name
// → ["Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Alla radartiklars enhetspriser
$.order.lineItems[*].unitPrice
// → [89.99, 12.49, 34.00]
// Alla radartiklars SKU-koder
$.order.lineItems[*].sku
// → ["WH-1042", "CB-USB-C", "SC-PRO-7"]Det här är mönstret du kommer att använda mest när du arbetar med arrayer i API-svar. Istället för att mappa över en array i din applikationskod kan du extrahera exakt det fält du behöver innan det ens når din affärslogik.
Rekursiv sökning — hitta värden på vilken djupnivå som helst
Operatorn .. gör en rekursiv sökning genom hela dokumentträdet.
Det är som en djupet-först-genomgång som samlar varje nod som matchar nyckelnamnet, oavsett var den befinner sig:
// Hitta varje "name"-fält var som helst i dokumentet
$..name
// → ["Maria Chen", "Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Hitta varje "city"-fält var som helst
$..city
// → ["Portland"]
// Hitta varje fält som heter "id" på vilken djupnivå som helst
$..id
// → ["ORD-9182", "CUST-441"]Lägg märke till hur $..name hittar både kundnamnet och alla produktnamn —
det bryr sig inte om djupet. Det här är kraftfullt för schemaexploration: när du får ett
okänt JSON-klump och vill hitta alla värden för en viss nyckel utan att känna till strukturen.
Om du vill dubbelkolla JSON-filen först är JSON Validator
och JSON Formatter praktiska startpunkter.
Arrayslicing
JSONPath lånar Python-stilens slice-notation för arrayer. Formatet är
[start:end:step], där vilken del som helst kan utelämnas.
Detta dokumenteras i RFC 9535 § 2.3.5:
// Första två radartiklarna
$.order.lineItems[0:2]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Från index 1 till slutet
$.order.lineItems[1:]
// → [{ sku: "CB-USB-C", ... }, { sku: "SC-PRO-7", ... }]
// Endast sista elementet (slice-syntax — brett stödd)
$.order.lineItems[-1:]
// → [{ sku: "SC-PRO-7", ... }]
// Vartannat element (steg 2)
$.order.lineItems[0::2]
// → [{ sku: "WH-1042", ... }, { sku: "SC-PRO-7", ... }]Filteruttryck — fråga med villkor
Det är här JSONPath verkligen visar sin styrka. Filtersyntaxen [?(...)]
låter dig fråga arrayobjekt efter deras fältvärden. Symbolen @ refererar till det
aktuella objektet som testas. Stefan Goessners
ursprungliga artikel från 2007
introducerade den här syntaxen, och RFC 9535 formaliserade den:
// Radartiklar under $50
$.order.lineItems[?(@.unitPrice < 50)]
// → [{ sku: "CB-USB-C", unitPrice: 12.49, ... }, { sku: "SC-PRO-7", unitPrice: 34.00, ... }]
// Endast artiklar som finns i lager
$.order.lineItems[?(@.inStock == true)]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Artiklar där qty är större än 1
$.order.lineItems[?(@.qty > 1)]
// → [{ sku: "CB-USB-C", qty: 2, ... }]
// Artiklar som INTE finns i lager
$.order.lineItems[?(@.inStock == false)].name
// → ["Phone Stand Pro"]Du kan också kombinera villkor. De flesta implementationer stöder && och ||
inuti filteruttryck, vilket låter dig skriva saker som
[?(@.inStock == true && @.unitPrice < 30)].
Kontrollera din biblioteksdokumentation — beteendet kring operatörsstöd var en av de viktigaste inkonsekvenserna
mellan pre-RFC-implementationer som
RFC 9535-standardiseringen syftade till att åtgärda.
[?(@.fieldName)]. Om till exempel
vissa radartiklar hade ett discountCode-fält och andra inte,
skulle $.order.lineItems[?(@.discountCode)] returnera bara de rabatterade.Använda JSONPath i JavaScript
Det finns ännu inget inbyggt JSONPath-stöd i JavaScript (till skillnad från XPath, som har
document.evaluate() i webbläsaren). Du behöver ett bibliotek.
De två mest populära är jsonpath
och jsonpath-plus.
Paketet jsonpath-plus underhålls mer aktivt och har bättre RFC 9535-anpassning:
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.88Notera att JSONPath() alltid returnerar en array — även när du frågar efter ett enda värde.
Det är designat så: ett sökvägsuttryck är en selektor, och en selektor kan matcha noll, ett eller många noder.
Så ta alltid result[0] när du vet att du riktar in dig på ett unikt värde.
Använda JSONPath i Python
I Python är biblioteket
jsonpath-ng
det mest kompletta alternativet. Det stöder kärnspecifikationen plus det mesta av filteruttryckssyntax:
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önstret parse() + find() är rätt tillvägagångssätt för produktionsanvändning —
kompilera uttrycket en gång och kör det mot många dokument, snarare än att återparsea sökvägssträngen varje gång.
jsonpath-ng-dokumentationen på GitHub
har mer detaljer om utökad filtersyntax och metoden update() för att modifiera matchade noder.
JSONPath vs jq — vilket bör du använda?
Folk blandar ibland ihop JSONPath och jq — båda "frågar JSON", men de löser olika problem. Här är den praktiska uppdelningen:
- JSONPath är ett sökvägsuttrycksspråk designat för att bäddas in i applikationer. Det frågar och extraherar värden. Du använder det inuti JavaScript-, Python- eller Java-kod, eller konfigurationsfiler som Kubernetes-selektorer.
- jq är en fullständig kommandoradsprocessor med sitt eget programmeringsspråk. Det kan fråga, transformera, omforma, filtrera och beräkna nya värden. Det är rätt verktyg för skalskript och engångsdatabearbetning i en terminal.
- Använd JSONPath när du skriver applikationskod som programmatiskt behöver extrahera fält från JSON — särskilt när biblioteket måste vara inbäddningsbart och lättviktigt.
- Använd jq när du är i kommandoraden, skriver skalskript, debuggar API-svar, eller behöver göra transformationer (byta namn på fält, aggregera, bygga nya strukturer) som går bortom enkel extraktion.
- jq-syntax är mer kraftfull men också mer komplex —
jq '.order.lineItems[] | select(.inStock) | .name'vs JSONPath:s$.order.lineItems[?(@.inStock)].name. För ren extraktion är JSONPath ofta mer läsbar.
Det finns också en mittväg: om du redan arbetar i JavaScript och bara behöver enstaka
transformationer täcker något som Lodash:s _.get()
enkel sökvägsåtkomst utan beroenden. Men för allt som involverar jokertecken, rekursion
eller filteruttryck är ett ordentligt JSONPath-bibliotek värt det.
Sammanfattning
JSONPath fyller ett verkligt gap mellan "traversera detta objekt manuellt i en for-loop" och
"starta en jq-process". När du väl är bekväm med $, ., [*],
.. och [?(filter)] kommer du att sträcka dig efter det hela tiden —
särskilt när du arbetar med stora API-nyttolaster där du bara behöver en handfull fält.
RFC 9535-standardiseringen
innebär att syntaxen nu är stabil, så uttryck du skriver idag bör fungera konsekvent över
kompatibla bibliotek.
Prova dina uttryck med JSON Path-verktyget — klistra in din JSON,
skriv en sökväg och se resultaten direkt. Och om din JSON behöver städas upp först,
är JSON Formatter och JSON Validator
precis där också.