Python JSONPath: So können Sie mit Python JSON-Dateien parsen

JSON ist derzeit eines der wichtigsten Formate für den Datenaustausch zwischen Anwendungen, vor allem über das Internet. JSONPath ist eine Ausdruckssprache, mit der man spezifische Daten aus JSON-Objekten lesen kann. Wir stellen Ihnen die Python-Implementierung von JSONPath vor und erklären die wichtigsten Anwendungen anhand von leicht verständlichen Beispielen.

Was ist Python JSONPath?

JSON ist ein systemübergreifendes Dateienformat, mit dem man den Austausch von strukturierten Daten zwischen Anwendungen vereinfachen und optimieren kann. JSON-Dateien bestehen aus aufgelisteten Schlüssel-Wert Paaren (Englisch: Key-Value Pairs). Die Werte können verschiedene Datentypen annehmen, sowohl primitive Werte als auch Objekte. Objekte können wiederum ihre eigene Schlüssel-Wert Paare enthalten. Da JSON von fast allen modernen Systemen verstanden wird, kann es für den Datenaustausch zwischen jeder Art von Anwendung verwendet werden, sowohl lokal auf dem Rechner als auch über das Internet.

Jedoch braucht nicht jede Anwendung immer alle Daten, die in einer JSON-Datei erfasst sind. In solchen Fällen bietet sich JSONPath an. JSONPath ist eine Ausdruckssprache mit der man gezielt spezifische Informationen aus JSON-Objekten lesen kann. In den meisten Programmiersprachen muss JSONPath aus einer externen Bibliothek importiert werden. Da diese Bibliotheken für jede Sprache separat implementiert werden müssen, können die unterschiedlichen Bibliotheken bzw. Implementierungen leicht voneinander abweichen.

Das Python jsonpath-ng Modul

jsonpath-ng ist die wohl gängigste Python Implementierung von JSONPath. Es gibt auch andere JSONPath-Implementierungen für Python, wie zum Beispiel jsonpath und jsonpath-rw. Diese sind allerdings weniger beliebt und umfassend, also konzentrieren wir uns hier ausschließlich auf jsonpath-ng.

Installation

jsonpath-ng können Sie sehr leicht über Ihre Shell installieren. Dafür müssen Sie nur das Kommando pip install jsonpath-ng eingeben.

Hinweis

Die Installation erfolgt über den Paketmanager pip, der für Python standardmäßig verwendet wird. Falls Sie diesen Paketmanager nicht auf Ihrem System installiert haben, müssen Sie dies zunächst tun. Weitere Informationen dazu finden Sie auf der Webseite von Pip.

Syntax

Mit JSONPath kann man komplexe Abfragen auf JSON-Objekten ausführen. Um dies zu ermöglichen, gibt es im Modul mehrere Methoden, Operatoren, und atomare Ausdrücke, mit denen spezifische Daten selektiert und abgefragt werden können. Die zwei wichtigsten JSONPath Methoden sind parse() und find(). Mit parse() kann man Abfragen definieren, die dann beliebig oft referenziert und wiederholt werden können. Mit find() kann man diese Abfragen auf JSON-Daten ausführen, um konkrete Werte zu extrahieren. Das folgende Beispiel dient zur Veranschaulichung.

import json
import jsonpath_ng as jp
raw_data = '''
{
    "name": "Johannes",
    "alter": 30,
    "wohnort": "Bielefeld"
}
'''
json_object = json.loads(raw_data)
name_query = jp.parse("$.name")
result = name_query.find(json_object)
print(result[0].value) # Ausgabe: Johannes 
Python

Im Beispiel oben wurden JSON-Daten in Form eines Strings mittels json.loads in ein Dictionary-Objekt umgewandelt. Dies ist das Format, mit dem Python am besten arbeiten kann. Beim Anlegen von name_query wurde die Anfrage "$.name" definiert, die den Wert von “name” zurückgeben soll. Diese wurde dann anschließend mit find() auf dem JSON-Objekt angewandt. Das Ergebnis der Anfrage wurde in der Variable result gespeichert und mit result[0].value ausgelesen.

Hinweis

Damit Python JSON-Daten aus einem String oder aus einer JSON-Datei lesen kann, muss zusätzlich das Python-Modul json eingebunden werden, wie im Beispiel oben zu sehen ist. Strings und Dateien können dann mittels loads() oder load() in ein für Python lesbares Format umgewandelt werden.

Die find-Methode liefert nicht nur den angefragten Wert zurück, sondern auch weitere kontextuelle Informationen, wie zum Beispiel der Pfad zum gesuchten Wert. Diese Informationen werden in Form einer Liste zurückgegeben, wobei der gesuchte Wert den Index 0 hat. Entsprechend können Sie mit result[0].value den gesuchten Wert ausgeben lassen.

Im oben genannten Beispiel wurde beim Festlegen der Anfrage das Dollarzeichen verwendet. Dies ist ein atomarer Ausdruck, mit dem man auf das Root-Objekt der JSON verweist. Alle Operatoren und atomare Ausdrücke sind in folgender Tabelle aufgelistet.

Ausdruck/Operator Bedeutung Beispiel Erklärung
$ Root-Objekt $.marcus.alter Greift auf dem Wert des Schlüssels “alter” aus dem Objekt “marcus” zu.
. Feld eines Objekts $.marcus Greift auf “marcus” zu, wobei “marcus” ein Feld des Root-Objekts ist.
.. Rekursive Suche nach einem Feld. Felder in Unterobjekten werden auch untersucht. $.people..alter Gibt alle Vorkommen des Feldes “alter” in “people” und seine Unterobjekte zurück.
[x] Element in einem Array $.people[5] Greift auf das sechste Element (an Index 5) im Array “people” zu.
\* Platzhalter für Zahl, wird meistens in Zusammenhang mit for-Schleifen verwendet $.people[\*] Greift auf ein Feld in “people” zu. In Kombination mit einer for-Schleife würde jedes Feld der Reihe nach zurückgegeben werden.

Zusätzlich zu den Ausdrücken und Operatoren gibt es noch einige Filter, mit denen man seine Suche noch spezifischer gestalten kann. In der Python Implementierung von JSONPath lassen sich diese mit Python-Operatoren verknüpfen. Alle Symbole, die in Zusammenhang mit Filtern verwendet werden können, sind mit Beispielen in folgender Tabelle aufgefasst.

Symbole Bedeutung Beispiel Erklärung
.[?(filter)] Allgemeine Syntax für Filter. Runde Klammern können weggelassen werden. $.people[?(@.name == "Anne")] Greift auf Personen zu, deren Namen “Anne” ist.
@ Aktuell untersuchtes Objekt, oft in Zusammenhang mit for-Schleifen verwendet. $.people[?(@.alter < 50)] Greift auf Felder in “people” zu, deren Wert für “alter” kleiner ist als 50.
<, >, <=, >=, == und != Vergleichsoperatoren, mit denen man bestimmte Suchergebnisse herausfiltern kann. $.people[@.alter < 50 & @.alter > 20] Greift auf Personen zu, deren Alter zwischen 20 und 50 Jahren liegt.
& Logisches UND. $.people[?(@.wohnort == 'Berlin' & @.alter > 40)] Greift auf Personen zu, die älter als 40 sind und in Berlin wohnen.
Hinweis

Wenn Sie Filter verwenden möchten, müssen Sie das Modul jsonpath_ng.ext einbinden, und beim Aufruf von parse() auf dieses verweisen.

Anwendungsbeispiel für Python JSONPath

import json
import jsonpath_ng as jp
import json
import jsonpath_ng as jp
# JSON-Daten als String
data = """
{
    "staedte": [
        {
            "name": "Berlin",
            "bundesland": "Berlin",
            "einwohner": 3645000,
            "istHauptstadt": true,
            "bezirk Pankow": {
                "einwohner": 410000    
            }
        },
        {
            "name": "Hamburg",
            "bundesland": "Hamburg",
            "einwohner": 1841000,
            "istHauptstadt": false
        },
        {
            "name": "Muenchen",
            "bundesland": "Bayern",
            "einwohner": 1472000,
            "istHauptstadt": false
        },
        {
            "name": "Koeln",
            "bundesland": "Nordrhein-Westfalen",
            "einwohner": 1086000
        }
    ]
}
"""
# Konvertiere data von String zu Dictionary
json_data = json.loads(data)
# Anfrage: Namen aller Städte
query1 = jp.parse("staedte[*].name")
for match in query1.find(json_data):
    print(match.value)     # Ausgabe: Berlin, Hamburg, Muenchen, Koeln
# jsonpath_ng.ext importieren, um Filter anzuwenden
import jsonpath_ng.ext as jpx
# Anfrage: Namen aller Städte, die weniger als 1,5 Millionen Einwohner haben 
query2 = jpx.parse("$.staedte[?@.einwohner < 1500000].name")
for match in query2.find(json_data):
    print(match.value)     # Ausgabe: Muenchen, Koeln
# Alle Felder, die mit "einwohner" beschriftet sind 
query3 = jp.parse("$.staedte..einwohner")
match = query3.find(json_data)
for i in match:
    print(i.value)     # Ausgabe: 3645000, 410000, 1841000, 1472000, 1086000
# Die Namen aller Städte, die nicht "Berlin" heißen
query4 = jpx.parse('$.staedte[?(@.name != "Berlin")].name')
for match in query4.find(json_data):
    print(match.value)     # Ausgabe: Hamburg, Muenchen, Koeln
Python

In diesem Beispiel werden JSON-Daten als String angegeben und dann mittels loads() in ein Dictionary-Objekt umgewandelt. Im Root-Objekt ist nur ein einzelnes Array enthalten, das wiederum 4 Städte enthält. Jede Stadt hat 4 Felder, die die folgenden Daten enthalten:

  • Name der Stadt
  • Bundesland der Stadt
  • Anzahl an Einwohner
  • Ob die Stadt die Hauptstadt Deutschlands ist oder nicht

Berlin hat als zusätzliches Feld noch ein Objekt namens “Bezirk Pankow”, das selbst auch eine Einwohneranzahl enthält.

Nachdem die Daten in ein passendes Format umgewandelt wurden, werden 4 unterschiedliche Abfragen ausgeführt, deren Funktionsweisen und Ausgaben im Beispiel als Kommentare hinterlassen sind. Sicher fällt Ihnen auf, dass bei der dritten Anfrage fünf Werte zurückgekommen sind. Das liegt daran, dass der ..-Operator rekursiv nach passenden Feldern sucht. Das heißt, es werden sowohl alle Objekte untersucht als auch alle Kinder dieser Objekte. Entsprechend ist die Einwohneranzahl von Pankow neben den Einwohneranzahlen der Städte aufgelistet.

Tipp

Die Kombination von JSON und Python ist ein vielseitiges Werkzeug für die Internetprogrammierung. Wenn Sie eine Webanwendung haben, die Sie schnell, leicht und direkt über Git veröffentlichen möchten, ist Deploy Now von IONOS die perfekte Lösung für Sie.