TATECHATLAS
◎ Deutsch
Programmierung

Python: Lesen von JSON aus Strings und Dateien und Fehlerbehandlung beim Dekodieren

Erfahren Sie, wie Sie JSON-Daten in Python sowohl aus String-Literalen als auch aus Dateien parsen und wie Sie potenzielle Dekodierungsfehler mit dem integrierten `json`-Modul effektiv behandeln.

Auf dieser Seite

Das `json`-Modul von Python bietet `json.loads()` zum Parsen von JSON aus Strings und `json.load()` zum Parsen aus dateiähnlichen Objekten. Beide Funktionen können `json.JSONDecodeError` auslösen, wenn die Eingabe kein gültiges JSON ist. Dies kann mithilfe eines `try-except`-Blocks abgefangen werden, um fehlerhafte Daten ordnungsgemäß zu behandeln.

Lesen von JSON aus einem String

Die Funktion `json.loads()` wird verwendet, um einen JSON-String in ein Python-Objekt zu deserialisieren. Sie nimmt einen String als Eingabe und gibt die entsprechende Python-Datenstruktur zurück (z. B. Wörterbücher, Listen, Strings, Zahlen, Booleans, None). Dies ist nützlich, wenn Sie JSON-Daten direkt in Ihrem Python-Code haben oder als String aus einer API-Antwort erhalten.

Um beispielsweise ein einfaches JSON-Objekt, das als String dargestellt wird, zu parsen, würden Sie den String an `json.loads()` übergeben.

import json

json_string = '{"name": "Alice", "age": 30, "isStudent": false}'

try:
    data = json.loads(json_string)
    print(data)
    print(f"Name: {data['name']}, Alter: {data['age']}")
except json.JSONDecodeError as e:
    print(f"Fehler beim Dekodieren des JSON-Strings: {e}")

Lesen von JSON aus einer Datei

Wenn Ihre JSON-Daten in einer Datei gespeichert sind, sollten Sie die Funktion `json.load()` verwenden. Diese Funktion nimmt ein dateiähnliches Objekt (im Textmodus geöffnet) als Eingabe, liest die JSON-Daten daraus und deserialisiert sie in ein Python-Objekt. Es ist entscheidend, die Datei korrekt zu öffnen und sicherzustellen, dass sie als Text gelesen wird.

Die Anweisung `with open(...)` ist die empfohlene Methode zur Handhabung von Dateien, da sie sicherstellt, dass die Datei auch bei Fehlern ordnungsgemäß geschlossen wird. Die Funktion `json.load()` verarbeitet dann den Inhalt der Datei.

import json

# Angenommen, 'data.json' enthält: [{"id": 1, "value": "A"}, {"id": 2, "value": "B"}]
file_path = 'data.json'

try:
    with open(file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
        print(data)
except FileNotFoundError:
    print(f"Fehler: Datei nicht gefunden unter {file_path}")
except json.JSONDecodeError as e:
    print(f"Fehler beim Dekodieren von JSON aus der Datei: {e}")

Behandlung von JSON-Dekodierungsfehlern

JSON-Daten müssen einem strengen Format entsprechen. Wenn der Eingabe-String oder der Dateiinhalt kein gültiges JSON ist, löst das `json`-Modul von Python eine `json.JSONDecodeError` aus. Diese Ausnahme liefert Details darüber, wo das Parsen fehlgeschlagen ist, z. B. die Position und die Art des Fehlers.

Um diese Fehler ordnungsgemäß zu behandeln, sollten Sie Ihre Aufrufe von `json.loads()` oder `json.load()` in einen `try...except json.JSONDecodeError`-Block einschließen. Dies ermöglicht es Ihrem Programm, weiter zu laufen oder informative Meldungen an den Benutzer zu geben, anstatt abzustürzen.

import json

malformed_json_string = '{"key": "value", "another_key": }' # Fehlender Wert

try:
    json.loads(malformed_json_string)
except json.JSONDecodeError as e:
    print(f"Eine JSONDecodeError abgefangen: {e}")
    print(f"Fehler aufgetreten in Zeile {e.lineno}, Spalte {e.colno}")
    print(f"Fehlermeldung: {e.msg}")

Verständnis der Details von JSONDecodeError

Das `json.JSONDecodeError`-Ausnahmeobjekt enthält nützliche Attribute, die helfen können, das genaue Problem in Ihren JSON-Daten zu identifizieren. Dazu gehören `msg` (die Fehlermeldung), `doc` (der zu parsende Eingabe-String), `pos` (der Zeichenindex im Dokument, an dem der Fehler aufgetreten ist), `lineno` (die Zeilennummer) und `colno` (die Spaltennummer).

Durch die Untersuchung dieser Attribute können Sie spezifischere Rückmeldungen zu fehlerhaftem JSON geben, was bei der Fehlersuche hilft oder den Benutzer über das Datenqualitätsproblem informiert.

import json

json_string_with_error = '''
{
    "name": "Bob",
    "details": {
        "city": "New York",
        "zip": 10001 # Fehlende Komma hier
        "country": "USA"
    }
}
'''

try:
    json.loads(json_string_with_error)
except json.JSONDecodeError as e:
    print(f"Fehler: {e.msg}")
    print(f"Dokument: {e.doc[max(0, e.pos-10):e.pos+10]}...") # Kontext anzeigen
    print(f"Position: {e.pos}, Zeile: {e.lineno}, Spalte: {e.colno}")

Anpassen der JSON-Dekodierung

Die Funktionen `json.load()` und `json.loads()` bieten Parameter wie `object_hook`, `parse_float`, `parse_int` und `parse_constant`, um anzupassen, wie JSON-Daten dekodiert werden. Zum Beispiel kann `parse_float` verwendet werden, um Gleitkommazahlen in `Decimal`-Objekte anstelle von Standard-Floats zu deserialisieren, was für Finanzanwendungen nützlich ist, die präzise Dezimalarithmetik erfordern.

Ebenso kann `object_hook` dekodierte JSON-Objekte (Python-Wörterbücher) in benutzerdefinierte Klasseninstanzen umwandeln, was eine objektorientiertere Datenverarbeitung ermöglicht.

import json
import decimal

json_string_with_floats = '{"price": "19.99", "tax": "1.50"}'

# Verwenden Sie Decimal für präzises Parsen von Gleitkommazahlen
data = json.loads(json_string_with_floats, parse_float=decimal.Decimal)

print(f"Geparste Daten: {data}")
print(f"Typ von price: {type(data['price'])}")

# Beispiel mit object_hook zum Erstellen eines benutzerdefinierten Objekts
def dict_to_person(d):
    if '__class__' in d and d['__class__'] == 'Person':
        return Person(d['name'], d['age'])
    return d

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age})"

json_person = '{"__class__": "Person", "name": "Charlie", "age": 25}'
person_obj = json.loads(json_person, object_hook=dict_to_person)
print(f"Dekodiertes Objekt: {person_obj}")

Kodieren von Python-Objekten in JSON

Obwohl der Schwerpunkt auf dem Lesen von JSON liegt, ist die umgekehrte Operation erwähnenswert: das Kodieren von Python-Objekten in JSON-Strings oder -Dateien mit `json.dumps()` bzw. `json.dump()`. Diese Funktionen kümmern sich um die Konvertierung von Python-Datentypen in ihre JSON-Entsprechungen.

Sie unterstützen auch Anpassungen wie `default` für die Handhabung nicht serialisierbarer Typen, `indent` für das Pretty-Printing und `sort_keys` für die sortierte Ausgabe, was für die Fehlersuche und Lesbarkeit sehr hilfreich sein kann.

import json

python_data = {
    "name": "David",
    "scores": [95, 88, 76],
    "isActive": True,
    "metadata": None
}

# Kodieren in einen JSON-String mit Pretty-Printing
json_output_string = json.dumps(python_data, indent=4, sort_keys=True)
print("--- JSON String Ausgabe ---")
print(json_output_string)

# Kodieren in eine Datei (Beispiel mit StringIO zur Demonstration)
from io import StringIO
output_file = StringIO()
json.dump(python_data, output_file, indent=4)
output_file.seek(0) # Zurückspulen, um den Inhalt zu lesen
print("\n--- JSON Datei Ausgabe (simuliert) ---")
print(output_file.read())

Sicherheitsaspekte

Das Parsen von JSON-Daten aus nicht vertrauenswürdigen Quellen erfordert Vorsicht. Bösartig gestaltetes JSON kann so konzipiert sein, dass es übermäßige CPU- und Speicherressourcen verbraucht, was potenziell zu Denial-of-Service-Angriffen führen kann. Das `json`-Modul enthält Prüfungen wie `check_circular` (standardmäßig aktiviert), um unendliche Rekursionen bei zirkulären Referenzen zu verhindern.

Es wird empfohlen, die Größe der zu parsierenden Daten zu begrenzen, wenn mit externen oder nicht vertrauenswürdigen Eingaben gearbeitet wird. Bei sehr großen JSON-Dateien sollten Sie Streaming-Parser verwenden oder die Daten nach Möglichkeit in Blöcken verarbeiten, obwohl das Standard-`json`-Modul von Python hauptsächlich mit dem gesamten Dokument auf einmal arbeitet.

import json

# Beispiel für eine potenziell ressourcenintensive JSON-Struktur (vereinfacht)
# Ein wirklich bösartiger String wäre viel komplexer.
# Das json-Modul hat einige eingebaute Schutzmechanismen, aber große Eingaben sind immer noch ein Risiko.

# Beispiel für zirkuläre Referenz (löst RecursionError aus, wenn check_circular True ist)
# data = {}
# data['self'] = data
# try:
#     json.dumps(data, check_circular=True)
# except RecursionError as e:
#     print(f"Erwarteten Fehler für zirkuläre Referenz abgefangen: {e}")

# Die Begrenzung der Eingabegröße ist eine praktische Sicherheitsmaßnahme
max_size = 1024 * 1024 # 1 MB

json_input = '{"key": "value"}' # Ersetzen Sie dies durch die tatsächliche Eingabequelle

if len(json_input.encode('utf-8')) > max_size:
    print("Fehler: Die eingegebenen JSON-Daten überschreiten die maximal zulässige Größe.")
else:
    try:
        data = json.loads(json_input)
        print("JSON innerhalb der Größenbeschränkungen erfolgreich geparst.")
    except json.JSONDecodeError as e:
        print(f"Fehler beim Dekodieren von JSON: {e}")

Verwendung von `pathlib` für Dateipfade

Bei der Arbeit mit JSON-Dateien ist die Verwaltung von Dateipfaden oft notwendig. Das `pathlib`-Modul von Python bietet einen objektorientierten Ansatz für Dateisystempfade, der sauberer und robuster ist als die String-Manipulation mit `os.path`.

Sie können `Path`-Objekte erstellen und sie direkt mit `open()` und `json.load()` verwenden. Dies integriert Dateihandhabung und JSON-Parsing nahtlos. `pathlib` behandelt Pfadtrennzeichen korrekt über verschiedene Betriebssysteme hinweg.

import json
from pathlib import Path

# Definieren Sie den Pfad zur JSON-Datei mit pathlib
file_path = Path('config.json')

# Stellen Sie sicher, dass die Datei existiert und gültiges JSON enthält, z.B.:
# {"database": "localhost", "port": 5432}

try:
    # Verwenden Sie das Path-Objekt direkt mit open()
    with file_path.open('r', encoding='utf-8') as f:
        config_data = json.load(f)
        print(f"Konfiguration geladen: {config_data}")
        print(f"Datenbank-Host: {config_data.get('database')}")
except FileNotFoundError:
    print(f"Fehler: Konfigurationsdatei nicht gefunden unter {file_path}")
except json.JSONDecodeError as e:
    print(f"Fehler beim Dekodieren von JSON aus {file_path}: {e}")
except Exception as e:
    print(f"Ein unerwarteter Fehler ist aufgetreten: {e}")

Was Sie prüfen sollten

  • Überprüfen Sie, ob der JSON-String oder der Dateiinhalt gemäß den JSON-Spezifikationen korrekt formatiert ist (z. B. Schlüssel und Strings in doppelten Anführungszeichen, korrekte Verwendung von Kommas, geschweiften und eckigen Klammern).
  • Stellen Sie sicher, dass die mit `json.load()` verwendeten Dateipfade korrekt sind und die Datei über die entsprechenden Leseberechtigungen verfügt.
  • Implementieren Sie `try-except`-Blöcke um `json.loads()` und `json.load()`, um `json.JSONDecodeError` abzufangen und fehlerhaftes JSON ordnungsgemäß zu behandeln.
  • Erwägen Sie die Verwendung von `encoding='utf-8'`, wenn Sie Dateien zum Lesen von JSON öffnen, um potenzielle Kodierungsprobleme zu vermeiden.
  • Implementieren Sie für nicht vertrauenswürdige Daten Größenbeschränkungen für das eingegebene JSON, um Denial-of-Service-Risiken zu mindern.

Das `json`-Modul dekodiert JSON in Standard-Python-Typen. Für komplexe benutzerdefinierte Typen oder spezifische numerische Präzision (wie bei Finanzberechnungen) müssen Sie möglicherweise `object_hook` oder `parse_float` mit benutzerdefinierten Funktionen (z. B. `decimal.Decimal`) verwenden. Das Modul parst das gesamte JSON-Dokument in den Speicher, was bei extrem großen Dateien ein Problem darstellen kann; in solchen Fällen sind möglicherweise alternative Streaming-JSON-Parser erforderlich. Sicherheitshinweise zu nicht vertrauenswürdigen Quellen sind wichtig; validieren oder bereinigen Sie Eingaben nach Möglichkeit immer.

Quellen

  1. Python: json ↗
  2. Python: pathlib ↗
Nach oben ↑