Python og JSON er et naturligt par. Uanset om du bygger et REST API med FastAPI eller Django, behandler datapipelines eller blot læser en konfigurationsfil — du vil arbejde med JSON konstant. Den gode nyhed: Pythons standardbibliotek har alt, hvad du behøver, i json-modulet. Ingen pip install nødvendig.

De fire funktioner du faktisk bruger

Modulet json giver dig fire funktioner til det daglige arbejde:

  • json.loads(str) — parser en JSON-streng til et Python-objekt
  • json.dumps(obj) — konverterer et Python-objekt til en JSON-streng
  • json.load(file) — parser JSON direkte fra et fil-objekt
  • json.dump(obj, file) — skriver et Python-objekt som JSON til en fil

Bogstavet s i loads / dumps står for string (streng). Dem uden s arbejder med filobjekter. Let at huske, når du kender reglen.

json.loads() — Parser en JSON-streng

python
import json

json_string = '{"name": "Alice", "age": 30, "active": true, "score": 98.5}'

user = json.loads(json_string)

print(user["name"])    # Alice
print(user["age"])     # 30
print(user["active"])  # True
print(type(user))      # <class 'dict'>

Bemærk typemapping: JSON true bliver Python True, JSON false bliver Python False, JSON null bliver Python None. JSON-objekter bliver Python-dict, JSON-arrays bliver Python-list.

json.dumps() — Serialisering til en JSON-streng

python
import json

user = {
    "name": "Bob",
    "age": 25,
    "roles": ["admin", "editor"],
    "active": True,
    "extra": None
}

# Kompakt (god til netværkstransmission)
compact = json.dumps(user)
print(compact)
# {"name": "Bob", "age": 25, "roles": ["admin", "editor"], "active": true, "extra": null}

# Pænt formateret (god til logs og menneskelig inspektion)
pretty = json.dumps(user, indent=2)
print(pretty)
# {
#   "name": "Bob",
#   "age": 25,
#   "roles": [
#     "admin",
#     "editor"
#   ],
#   "active": true,
#   "extra": null
# }

Bemærk den omvendte typemapping: Python True → JSON true, Python None → JSON null. Python håndterer dette automatisk.

Læsning af JSON fra en fil

Dette er sandsynligvis det mest almindelige brugstilfælde — læsning af en konfigurationsfil eller datafil ved opstart:

python
import json

# Læs og parser i ét trin
with open("config.json", "r", encoding="utf-8") as f:
    config = json.load(f)

print(config["database"]["host"])  # localhost
print(config["database"]["port"])  # 5432

Angiv altid encoding="utf-8" når du åbner JSON-filer. JSON er specificeret som UTF-8 af RFC 8259, og at udelade det kan forårsage problemer på Windows, hvor standardkodningen nogle gange er cp1252.

Skrivning af JSON til en fil

python
import json

results = {
    "timestamp": "2024-01-15T09:30:00Z",
    "total": 1523,
    "processed": 1521,
    "failed": 2,
    "errors": [
        {"id": 42, "reason": "missing field"},
        {"id": 99, "reason": "invalid format"}
    ]
}

with open("results.json", "w", encoding="utf-8") as f:
    json.dump(results, f, indent=2)

print("Results saved to results.json")

Korrekt fejlhåndtering

json.loads() kaster json.JSONDecodeError (en underklasse til ValueError), når input ikke er gyldig JSON. Håndter altid dette, når du parser data, du ikke kontrollerer:

python
import json

def safe_parse(json_str):
    try:
        return json.loads(json_str)
    except json.JSONDecodeError as e:
        print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")
        return None

data = safe_parse('{"name": "Alice"}')   # fungerer fint
bad  = safe_parse('not json at all')     # udskriver fejl, returnerer None
also_bad = safe_parse('{"key": }')       # udskriver fejl med positionsinfo

JSONDecodeError giver dig den præcise linje og kolonne, hvor parsing mislykkedes — nyttigt ved fejlsøgning af store JSON-filer.

Nyttige dumps()-indstillinger

python
import json

data = {
    "z_key": 1,
    "a_key": 2,
    "price": 9.999999999
}

# Sorter nøgler alfabetisk (godt til reproducerbart output / diffs)
print(json.dumps(data, sort_keys=True, indent=2))
# {
#   "a_key": 2,
#   "price": 9.999999999,
#   "z_key": 1
# }

# Bevar ikke-ASCII-tegn (standard: escaped til \uXXXX)
data2 = {"city": "Münich", "greeting": "こんにちは"}
print(json.dumps(data2, ensure_ascii=False))
# {"city": "Münich", "greeting": "こんにちは"}

# Med ensure_ascii=True (standard):
print(json.dumps(data2))
# {"city": "M\u00fcnich", "greeting": "\u3053\u3093\u306b\u3061\u306f"}

ensure_ascii=False er noget, jeg altid tilføjer, når jeg skriver JSON-filer, der indeholder ikke-ASCII-tekst. Den escapede version er teknisk set gyldig JSON, men meget sværere at læse i en teksteditor.

Serialisering af brugerdefinerede objekter

Som standard kan json.dumps() ikke serialisere brugerdefinerede klasseinstanser eller datetime-objekter. Du har to muligheder: arv fra json.JSONEncoder, eller konverter til en dict først:

python
import json
from datetime import datetime, date

# Mulighed 1: brugerdefineret koderklasse
class AppEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime, date)):
            return obj.isoformat()
        return super().default(obj)

data = {"name": "Alice", "created_at": datetime(2024, 1, 15, 9, 30)}
print(json.dumps(data, cls=AppEncoder, indent=2))
# {
#   "name": "Alice",
#   "created_at": "2024-01-15T09:30:00"
# }

# Mulighed 2: parameteren default= (enklere til engangkonverteringer)
print(json.dumps(data, default=str, indent=2))  # konverterer alt ukendt til str

Et praktisk mønster: indlæsning af konfigurationsfil

Her er et virkeligt mønster, jeg bruger i næsten alle Python-projekter — en konfigurationsindlæser der læser en JSON-konfigfil med fornuftige standardværdier:

python
import json
import os
from pathlib import Path

DEFAULTS = {
    "database": {"host": "localhost", "port": 5432},
    "debug": False,
    "log_level": "INFO"
}

def load_config(path="config.json"):
    config = DEFAULTS.copy()

    config_path = Path(path)
    if config_path.exists():
        with open(config_path, "r", encoding="utf-8") as f:
            try:
                user_config = json.load(f)
                # Dyb sammenfletning: brugerindstillinger tilsidesætter standarder
                for key, value in user_config.items():
                    if isinstance(value, dict) and key in config:
                        config[key].update(value)
                    else:
                        config[key] = value
            except json.JSONDecodeError as e:
                print(f"Warning: config.json is invalid ({e.msg}), using defaults")

    return config

config = load_config()
print(config["database"]["host"])  # localhost (eller tilsidesat værdi)

Opsummering

Pythons json-modul dækker alt, hvad du behøver, uden nogen afhængigheder. Nøglereglerne: brug loads()/dumps() til strenge, load()/dump() til filer, håndter altid JSONDecodeError ved parsing af eksterne data, og tilføj ensure_ascii=False, når dine data indeholder ikke-latinske tegn. Til fejlsøgning af JSON-data kan JSON Formatter og JSON Validator spare dig meget tid.