At­tual­men­te JSON è uno dei formati più im­por­tan­ti per lo scambio di dati fra ap­pli­ca­zio­ni, spe­cial­men­te sul web. JSONPath è un lin­guag­gio di query che consente di estrarre dati specifici da oggetti JSON. In questo contesto ti spie­ghia­mo come im­ple­men­ta­re JSONPath in Python e ti pro­po­nia­mo alcuni esempi facili da capire.

Cos’è JSONPath di Python?

JSON è un formato di file in­ter­si­ste­ma, uti­liz­za­to per agevolare e mi­glio­ra­re lo scambio di dati strut­tu­ra­ti tra ap­pli­ca­zio­ni. I file JSON sono co­sti­tui­ti da coppie chiave-valore elencate (dall’inglese: “key-value pairs”). I valori in JSON possono essere espressi con diversi tipi di dati, compresi valori primitivi e oggetti. Gli oggetti, a loro volta, possono contenere le proprie coppie chiave-valore. Grazie alla sua ampia adozione nei sistemi moderni, JSON è adatto per il tra­sfe­ri­men­to di dati tra ap­pli­ca­zio­ni, sia a livello locale che su internet.

Tuttavia, non tutte le ap­pli­ca­zio­ni hanno bisogno di tutti i dati presenti in un file JSON. In questi casi si può usare JSONPath. JSONPath è un lin­guag­gio di query che consente di estrarre in modo mirato in­for­ma­zio­ni spe­ci­fi­che da oggetti JSON. Nella maggior parte dei linguaggi di pro­gram­ma­zio­ne, JSONPath deve essere importato da una libreria esterna. Poiché tali librerie devono essere im­ple­men­ta­te se­pa­ra­ta­men­te per ogni lingua, le diverse librerie possono differire leg­ger­men­te fra loro.

Il modulo jsonpath-ng in Python

jsonpath-ng è l’im­ple­men­ta­zio­ne più comune di JSONPath. Esistono anche altre im­ple­men­ta­zio­ni di JSONPath per Python, come ad esempio jsonpath e jsonpath-rw. Tuttavia, queste sono meno note e complete, per cui in questo articolo ci con­cen­tre­re­mo solo su jsonpath-ng.

In­stal­la­zio­ne

Puoi fa­cil­men­te in­stal­la­re jsonpath-ng dalla shell. Per farlo basta digitare il comando pip install jsonpath-ng.

N.B.

L’in­stal­la­zio­ne si effettua uti­liz­zan­do il gestore di pacchetti pip, pre­de­fi­ni­to per Python. Prima di iniziare è im­por­tan­te as­si­cu­rar­si che pip sia in­stal­la­to nel sistema e nel caso in cui non sia presente, è ne­ces­sa­rio procedere con l’in­stal­la­zio­ne. Per ulteriori in­for­ma­zio­ni visita il sito web di pip.

Sintassi

Puoi usare JSONPath per eseguire query complesse su oggetti JSON. A tal fine, nel modulo sono presenti diversi metodi, operatori ed espres­sio­ni atomiche che possono essere uti­liz­za­ti per se­le­zio­na­re e in­ter­ro­ga­re dati specifici. I due metodi prin­ci­pa­li di JSONPath sono parse() e find(). parse() consente di definire le query che possono poi essere re­fe­ren­zia­te varie volte. Con find() è possibile eseguire queste query su dati JSON per estrarre valori concreti. Per chiarire meglio il concetto guardiamo l’esempio qui sotto.

import json
import jsonpath_ng as jp
raw_data =  '''
{
    "nome": "Johannes",
    "età": 30,
    "città di residenza": "Como"
}
'''
json_object = json.loads(raw_data)
nome_query = jp.parse("$.nome")
result = nome_query.find(json_object)
print(result[0].value) # Output: Johannes
Python

Nell’esempio abbiamo tra­sfor­ma­to i dati JSON, espressi sotto forma di stringa, in un oggetto di­zio­na­rio uti­liz­zan­do la funzione json.loads. Questo è il formato più adatto per lavorare con Python. Nel momento in cui abbiamo creato l’oggetto nome_query, abbiamo definito la query "$.nome" per ottenere il valore cor­ri­spon­den­te a “nome”. Suc­ces­si­va­men­te, questa ope­ra­zio­ne è stata applicata all’oggetto JSON uti­liz­zan­do il metodo find(). Il risultato della query è stato assegnato alla variabile result e suc­ces­si­va­men­te re­cu­pe­ra­to tramite result[0].value.

N.B.

Per con­sen­ti­re a Python di in­ter­pre­ta­re dati JSON da una stringa o da un file JSON, è ne­ces­sa­rio includere il modulo Python json come evi­den­zia­to nell’esempio pre­ce­den­te. Suc­ces­si­va­men­te è possibile con­ver­ti­re le stringhe e i file in un formato che può essere letto da Python uti­liz­zan­do le funzioni loads() o load().

Il metodo find non fornisce solo il valore de­si­de­ra­to, ma include anche in­for­ma­zio­ni con­te­stua­li ag­giun­ti­ve, ad esempio il percorso del valore cercato. Tali in­for­ma­zio­ni sono re­sti­tui­te sotto forma di lista, in cui il valore cercato presenta l’indice 0. Pertanto, per ottenere il valore de­si­de­ra­to, è possibile uti­liz­za­re result[0].value.

Nell’esempio pre­ce­den­te, abbiamo uti­liz­za­to il simbolo del dollaro per definire la query. Questa è un’espres­sio­ne atomica con la quale si fa ri­fe­ri­men­to all’oggetto root di JSON. Tutti gli operatori e le pro­po­si­zio­ni logiche sono elencati nella seguente tabella.

Pro­po­si­zio­ne/Operatore Si­gni­fi­ca­to Esempio Spie­ga­zio­ne
$ Oggetto root $.marcus.età Accede al valore della chiave “età” nell’oggetto “marcus”.
. Campo di un oggetto $.marcus Accede a “marcus”, dove “marcus” è un campo dell’oggetto root.
.. Ricerca ricorsiva di un campo. Vengono esaminati anche i campi degli oggetti figli. $.people..età Ritorna tutte le oc­cor­ren­ze del campo “età” in “people” e nei suoi oggetti figli.
[x] Elemento di un array $.people[5] Accede al sesto elemento (indice 5) dell’array “people”.
\* Se­gna­po­sto per numero, usato so­prat­tut­to in com­bi­na­zio­ne con cicli for $.people[\*] Accede a un campo in “people”. In com­bi­na­zio­ne con un ciclo for, ogni campo viene ritornato in sequenza.

Oltre alle espres­sio­ni e agli operatori, ci sono alcuni filtri da usare per rendere la ricerca ancora più specifica. Nell’im­ple­men­ta­zio­ne Python di JSONPath è possibile collegare tali filtri anche con gli operatori Python. Tutti i simboli uti­liz­za­bi­li in relazione ai filtri sono riportati nella tabella sot­to­stan­te con degli esempi.

Simboli Si­gni­fi­ca­to Esempio Spie­ga­zio­ne
.[?(filter)] Sintassi generale per i filtri. Le parentesi tonde possono essere omesse. $.people[?(@.nome == "Anna")] Trova persone il cui nome è “Anna”.
@ Oggetto at­tual­men­te ana­liz­za­to, usato spesso in con­co­mi­tan­za con cicli for. $.people[?(@.età < 50)] Trova i campi “people” il cui valore relativo a “età” è inferiore a 50.
<, >, <=, >=, == e != Operatori di com­pa­ra­zio­ne grazie ai quali è possibile filtrare alcuni risultati di ricerca. $.people[@.età < 50 & @.età > 20] Trova persone la cui età è compresa fra i 20 e i 50 anni.
& E logico $.people[?(@.luogo di residenza == 'Roma' & @.età > 40)] Trova persone più vecchie di 40 anni e che vivono a Roma.
N.B.

Se desideri usare un filtro devi includere il modulo jsonpath_ng.ext e farvi ri­fe­ri­men­to quando chiami parse().

Esempio di utilizzo per JSONPath in Python

import json
import jsonpath_ng as jp
# Dati JSON come stringa
import json
import jsonpath_ng as jp
# Dati JSON come stringa
data = """
{
    "città": [
        {
            "nome": "Roma",
            "provincia": "Lazio",
            "abitanti": 2873000
            "èCapitale": true,
            "quartiere testaccio": {
                "abitanti": 7671    
            }
        },
        {
            "nome": "Milano",
            "provincia": "Lombardia",
            "abitanti": 1361908,
            "èCapitale": false
        },
        {
            "nome": "Firenze",
            "provincia": "Toscana",
            "abitanti": 382258,
            "èCapitale": false
        },
        {
            "nome": "Venezia",
            "provincia": "Veneto",
            "abitanti": 261905
        }
    ]
}
"""
# Converti dati da stringa a dizionario
json_data = json.loads(data)
# Query: nomi di tutte le città
query1 = jp.parse("città[*].nome")
for match in query1.find(json_data):
    print(match.value)     # Output: Roma, Milano, Firenze, Venezia
importare # jsonpath_ng.ext per utilizzare il filtro
import jsonpath_ng.ext as jpx
# Query: nome di tutte le città che hanno meno di 1 milione di abitanti 
query2 = jpx.parse("$.città[?@.abitanti < 1000000].nome")
for match in query2.find(json_data):
    print(match.value)     # Output: Firenze, Venezia
# Tutti i campi riportanti il titolo "abitanti" 
query3 = jp.parse("$.città..abitanti")
match = query3.find(json_data)
for i in match:
    print(i.value)     # Output: 2873000, 7671, 1361908, 382258, 261905
# Nome di tutte le città che non si chiamano "Roma"
query4 = jpx.parse('$.città[?(@.nome != "Roma")].nome')
for match in query4.find(json_data):
    print(match.value)     # Output: Milano, Firenze, Venezia
Python

In questo esempio, i dati JSON sono spe­ci­fi­ca­ti come stringa e poi con­ver­ti­ti nell’oggetto di­zio­na­rio uti­liz­zan­do loads(). Nell’oggetto root è compreso un solo array, che a sua volta contiene 4 città. Ciascuna città ha 4 campi, che a loro volta com­pren­do­no i seguenti dati:

  • Nome della città
  • Provincia della città
  • Numero di abitanti
  • Se la città è la capitale italiana o no

Come campo ac­ces­so­rio, Roma ha un oggetto in­ti­to­la­to “Quartiere Testaccio”, che a sua volta contiene un valore relativo al numero di abitanti.

Dopo aver con­ver­ti­to i dati in un formato adeguato, vengono eseguite 4 diverse query, le cui funzioni e risultati vengono inseriti nell’esempio sotto forma di commenti. Come potrai notare, la terza query re­sti­tui­sce cinque valori. Questo avviene perché l’operatore .. esegue una ricerca ricorsiva dei campi adatti. Ciò significa che vengono ana­liz­za­ti tutti gli oggetti e tutti i loro figli. Per questo motivo il numero di abitanti del quartiere Testaccio compare nella lista degli abitanti delle città.

Consiglio

La com­bi­na­zio­ne di JSON e Python è uno strumento versatile per la pro­gram­ma­zio­ne internet. Se stai lavorando a un’ap­pli­ca­zio­ne web e desideri pub­bli­car­la in modo rapido, facile e diretto via Git, Deploy Now di IONOS è la soluzione perfetta per te.

Vai al menu prin­ci­pa­le