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:

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
    }
  }
}

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:

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

text
// 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"
Obs angående negativt indexering: $[-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:

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

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

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

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

Kontrollera om ett fält finns: För att filtrera objekt som har ett visst fält (oavsett dess värde), använd existenskontrollsyntaxen: [?(@.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:

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

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

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

Mö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å.