5 nützliche DIY-Python-Funktionen für die JSON-Analyse und -Verarbeitung5 nützliche DIY-Python-Funktionen für die JSON-Analyse und -Verarbeitung
Bild vom Autor

# Einführung

Arbeiten mit JSON in Python ist oft eine Herausforderung. Das Grundlegende json.hundreds() bringt dich nur so weit.

API-Antworten, Konfigurationsdateien und Datenexporte enthalten häufig JSON, das chaotisch oder schlecht strukturiert ist. Sie müssen verschachtelte Objekte reduzieren und Werte ohne sie sicher extrahieren Schlüsselfehler Ausnahmen, mehrere JSON-Dateien zusammenführen oder zwischen JSON und anderen Formaten konvertieren. Diese Aufgaben fallen beim Internet Scraping, der API-Integration und der Datenverarbeitung ständig an. Dieser Artikel führt Sie durch fünf praktische Funktionen zur Handhabung gängiger JSON-Parsing- und -Verarbeitungsaufgaben.

Den Code für diese Funktionen finden Sie unter GitHub.

# 1. Verschachtelte Werte sicher extrahieren

JSON-Objekte sind oft mehrere Ebenen tief verschachtelt. Der Zugriff auf tief verschachtelte Werte mit Klammernotation wird schnell zu einer Herausforderung. Sollte ein Schlüssel fehlen, erhalten Sie einen Schlüsselfehler.

Hier ist eine Funktion, mit der Sie mithilfe der Punktnotation auf verschachtelte Werte zugreifen können, mit einem Fallback für fehlende Schlüssel:

def get_nested_value(information, path, default=None):
    """
    Safely extract nested values from JSON utilizing dot notation.

    Args:
        information: Dictionary or JSON object
        path: Dot-separated string like "person.profile.e-mail"
        default: Worth to return if path does not exist

    Returns:
        The worth on the path, or default if not discovered
    """
    keys = path.cut up('.')
    present = information

    for key in keys:
        if isinstance(present, dict):
            present = present.get(key)
            if present is None:
                return default
        elif isinstance(present, checklist):
            strive:
                index = int(key)
                present = present(index)
            besides (ValueError, IndexError):
                return default
        else:
            return default

    return present

Testen wir es mit einer komplexen verschachtelten Struktur:

# Pattern JSON information
user_data = {
    "person": {
        "id": 123,
        "profile": {
            "title": "Allie",
            "e-mail": "allie@instance.com",
            "settings": {
                "theme": "darkish",
                "notifications": True
            }
        },
        "posts": (
            {"id": 1, "title": "First Publish"},
            {"id": 2, "title": "Second Publish"}
        )
    }
}

# Extract values
e-mail = get_nested_value(user_data, "person.profile.e-mail")
theme = get_nested_value(user_data, "person.profile.settings.theme")
first_post = get_nested_value(user_data, "person.posts.0.title")
lacking = get_nested_value(user_data, "person.profile.age", default=25)

print(f"Electronic mail: {e-mail}")
print(f"Theme: {theme}")
print(f"First put up: {first_post}")
print(f"Age (default): {lacking}")

Ausgabe:

Electronic mail: allie@instance.com
Theme: darkish
First put up: First Publish
Age (default): 25

Die Funktion teilt die Pfadzeichenfolge in Punkte auf und durchläuft die Datenstruktur Schlüssel für Schlüssel. Auf jeder Ebene wird geprüft, ob der aktuelle Wert ein Wörterbuch oder eine Liste ist. Für Wörterbücher verwendet es .get(key)was zurückkommt None für fehlende Schlüssel, anstatt einen Fehler auszulösen. Bei Hear wird versucht, den Schlüssel in einen ganzzahligen Index umzuwandeln.

Der default Der Parameter bietet einen Fallback, wenn ein Teil des Pfads nicht vorhanden ist. Dies verhindert, dass Ihr Code abstürzt, wenn Sie unvollständige oder inkonsistente JSON-Daten von APIs verarbeiten.

Dieses Muster ist besonders nützlich bei der Verarbeitung von API-Antworten, bei denen einige Felder non-compulsory sind oder nur unter bestimmten Bedingungen vorhanden sind.

# 2. Reduzieren von verschachteltem JSON in einstufige Wörterbücher

Modelle für maschinelles Lernen, CSV-Exporte und Datenbankeinfügungen erfordern häufig flache Datenstrukturen. Aber API-Antworten und Konfigurationsdateien verwenden verschachteltes JSON. Das Konvertieren verschachtelter Objekte in flache Schlüssel-Wert-Paare ist eine häufige Aufgabe.

Hier ist eine Funktion, die verschachteltes JSON mit anpassbaren Trennzeichen flacher macht:

def flatten_json(information, parent_key='', separator="_"):
    """
    Flatten nested JSON right into a single-level dictionary.

    Args:
        information: Nested dictionary or JSON object
        parent_key: Prefix for keys (utilized in recursion)
        separator: String to affix nested keys

    Returns:
        Flattened dictionary with concatenated keys
    """
    gadgets = ()

    if isinstance(information, dict):
        for key, worth in information.gadgets():
            new_key = f"{parent_key}{separator}{key}" if parent_key else key

            if isinstance(worth, dict):
                # Recursively flatten nested dicts
                gadgets.lengthen(flatten_json(worth, new_key, separator).gadgets())
            elif isinstance(worth, checklist):
                # Flatten lists with listed keys
                for i, merchandise in enumerate(worth):
                    list_key = f"{new_key}{separator}{i}"
                    if isinstance(merchandise, (dict, checklist)):
                        gadgets.lengthen(flatten_json(merchandise, list_key, separator).gadgets())
                    else:
                        gadgets.append((list_key, merchandise))
            else:
                gadgets.append((new_key, worth))
    else:
        gadgets.append((parent_key, information))

    return dict(gadgets)

Lassen Sie uns nun eine komplexe verschachtelte Struktur reduzieren:

# Complicated nested JSON
product_data = {
    "product": {
        "id": 456,
        "title": "Laptop computer",
        "specs": {
            "cpu": "Intel i7",
            "ram": "16GB",
            "storage": {
                "sort": "SSD",
                "capability": "512GB"
            }
        },
        "opinions": (
            {"ranking": 5, "remark": "Glorious"},
            {"ranking": 4, "remark": "Good worth"}
        )
    }
}

flattened = flatten_json(product_data)

for key, worth in flattened.gadgets():
    print(f"{key}: {worth}")

Ausgabe:

product_id: 456
product_name: Laptop computer
product_specs_cpu: Intel i7
product_specs_ram: 16GB
product_specs_storage_type: SSD
product_specs_storage_capacity: 512GB
product_reviews_0_rating: 5
product_reviews_0_comment: Glorious
product_reviews_1_rating: 4
product_reviews_1_comment: Good worth

Die Funktion verwendet Rekursion, um beliebige Verschachtelungstiefen zu verarbeiten. Wenn es auf ein Wörterbuch stößt, verarbeitet es jedes Schlüssel-Wert-Paar und erstellt den abgeflachten Schlüssel, indem es die übergeordneten Schlüssel mit dem Trennzeichen verkettet.

Bei Hear wird der Index als Teil des Schlüssels verwendet. Dadurch können Sie die Reihenfolge und Struktur der Array-Elemente in der reduzierten Ausgabe beibehalten. Das Muster reviews_0_rating sagt Ihnen, dass dies die Bewertung aus der ersten Bewertung ist.

Der separator Mit dem Parameter können Sie das Ausgabeformat anpassen. Verwenden Sie je nach Bedarf Punkte für die Punktnotation, Unterstriche für „snake_case“ oder Schrägstriche für pfadähnliche Schlüssel.

Diese Funktion ist besonders nützlich, wenn Sie JSON-API-Antworten in Datenrahmen oder CSV-Zeilen konvertieren müssen, in denen jede Spalte einen eindeutigen Namen benötigt.

# 3. Deep Merge mehrerer JSON-Objekte

Für die Konfigurationsverwaltung müssen häufig mehrere JSON-Dateien zusammengeführt werden, die Standardeinstellungen, umgebungsspezifische Konfigurationen, Benutzereinstellungen und mehr enthalten. Ein einfaches dict.replace() Behandelt nur die oberste Ebene. Sie benötigen eine tiefe Zusammenführung, die verschachtelte Strukturen rekursiv kombiniert.

Hier ist eine Funktion, die JSON-Objekte tief zusammenführt:

def deep_merge_json(base, override):
    """
    Deep merge two JSON objects, with override taking priority.

    Args:
        base: Base dictionary
        override: Dictionary with values to override/add

    Returns:
        New dictionary with merged values
    """
    end result = base.copy()

    for key, worth in override.gadgets():
        if key in end result and isinstance(end result(key), dict) and isinstance(worth, dict):
            # Recursively merge nested dictionaries
            end result(key) = deep_merge_json(end result(key), worth)
        else:
            # Override or add the worth
            end result(key) = worth

    return end result

Versuchen wir, Beispielkonfigurationsinformationen zusammenzuführen:

import json

# Default configuration
default_config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "timeout": 30,
        "pool": {
            "min": 2,
            "max": 10
        }
    },
    "cache": {
        "enabled": True,
        "ttl": 300
    },
    "logging": {
        "stage": "INFO"
    }
}

# Manufacturing overrides
prod_config = {
    "database": {
        "host": "prod-db.instance.com",
        "pool": {
            "min": 5,
            "max": 50
        }
    },
    "cache": {
        "ttl": 600
    },
    "monitoring": {
        "enabled": True
    }
}

merged = deep_merge_json(default_config, prod_config)

print(json.dumps(merged, indent=2))

Ausgabe:

{
  "database": {
    "host": "prod-db.instance.com",
    "port": 5432,
    "timeout": 30,
    "pool": {
      "min": 5,
      "max": 50
    }
  },
  "cache": {
    "enabled": true,
    "ttl": 600
  },
  "logging": {
    "stage": "INFO"
  },
  "monitoring": {
    "enabled": true
  }
}

Die Funktion führt verschachtelte Wörterbücher rekursiv zusammen. Wenn sowohl die Foundation als auch die Überschreibung Wörterbücher mit demselben Schlüssel enthalten, werden diese Wörterbücher zusammengeführt, anstatt sie vollständig zu ersetzen. Dadurch bleiben Werte erhalten, die nicht explizit überschrieben werden.

Beachten Sie, wie database.port Und database.timeout bleiben von der Standardkonfiguration, während database.host wird überschrieben. Die Pooleinstellungen werden additionally auf der verschachtelten Ebene zusammengeführt min Und max beide werden aktualisiert.

Die Funktion fügt auch neue Schlüssel hinzu, die in der Basiskonfiguration nicht vorhanden sind, wie z monitoring Abschnitt in der Produktionsüberschreibung.

Sie können mehrere Zusammenführungen zu Layer-Konfigurationen verketten:

final_config = deep_merge_json(
    deep_merge_json(default_config, prod_config),
    user_preferences
)

Dieses Muster kommt häufig in der Anwendungskonfiguration vor, wo Sie über Standardwerte, umgebungsspezifische Einstellungen und Laufzeitüberschreibungen verfügen.

# 4. JSON nach Schema oder Whitelist filtern

APIs geben oft mehr Daten zurück, als Sie benötigen. Große JSON-Antworten erschweren die Lesbarkeit Ihres Codes. Manchmal möchten Sie nur bestimmte Felder oder müssen vor der Protokollierung vertrauliche Daten entfernen.

Hier ist eine Funktion, die JSON filtert, um nur bestimmte Felder beizubehalten:

def filter_json(information, schema):
    """
    Filter JSON to maintain solely fields laid out in schema.

    Args:
        information: Dictionary or JSON object to filter
        schema: Dictionary defining which fields to maintain
                Use True to maintain a discipline, nested dict for nested filtering

    Returns:
        Filtered dictionary containing solely specified fields
    """
    if not isinstance(information, dict) or not isinstance(schema, dict):
        return information

    end result = {}

    for key, worth in schema.gadgets():
        if key not in information:
            proceed

        if worth is True:
            # Maintain this discipline as-is
            end result(key) = information(key)
        elif isinstance(worth, dict):
            # Recursively filter nested object
            if isinstance(information(key), dict):
                filtered_nested = filter_json(information(key), worth)
                if filtered_nested:
                    end result(key) = filtered_nested
            elif isinstance(information(key), checklist):
                # Filter every merchandise within the checklist
                filtered_list = ()
                for merchandise in information(key):
                    if isinstance(merchandise, dict):
                        filtered_item = filter_json(merchandise, worth)
                        if filtered_item:
                            filtered_list.append(filtered_item)
                    else:
                        filtered_list.append(merchandise)
                if filtered_list:
                    end result(key) = filtered_list

    return end result

Lassen Sie uns eine Beispiel-API-Antwort filtern:

import json
# Pattern API response
api_response = {
    "person": {
        "id": 789,
        "username": "Cayla",
        "e-mail": "cayla@instance.com",
        "password_hash": "secret123",
        "profile": {
            "title": "Cayla Smith",
            "bio": "Software program developer",
            "avatar_url": "https://instance.com/avatar.jpg",
            "private_notes": "Inside notes"
        },
        "posts": (
            {
                "id": 1,
                "title": "Hi there World",
                "content material": "My first put up",
                "views": 100,
                "internal_score": 0.85
            },
            {
                "id": 2,
                "title": "Python Suggestions",
                "content material": "Some ideas",
                "views": 250,
                "internal_score": 0.92
            }
        )
    },
    "metadata": {
        "request_id": "abc123",
        "server": "web-01"
    }
}

# Schema defining what to maintain
public_schema = {
    "person": {
        "id": True,
        "username": True,
        "profile": {
            "title": True,
            "avatar_url": True
        },
        "posts": {
            "id": True,
            "title": True,
            "views": True
        }
    }
}

filtered = filter_json(api_response, public_schema)

print(json.dumps(filtered, indent=2))

Ausgabe:

{
  "person": {
    "id": 789,
    "username": "Cayla",
    "profile": {
      "title": "Cayla Smith",
      "avatar_url": "https://instance.com/avatar.jpg"
    },
    "posts": (
      {
        "id": 1,
        "title": "Hi there World",
        "views": 100
      },
      {
        "id": 2,
        "title": "Python Suggestions",
        "views": 250
      }
    )
  }
}

Das Schema fungiert als Whitelist. Ein Feld festlegen auf True schließt es in die Ausgabe ein. Mithilfe eines verschachtelten Wörterbuchs können Sie verschachtelte Objekte filtern. Die Funktion wendet das Schema rekursiv auf verschachtelte Strukturen an.

Bei Arrays gilt das Schema für jedes Aspect. Im Beispiel wird das Posts-Array gefiltert, sodass jeder Publish nur Folgendes enthält: id, titleUnd viewswährend content material Und internal_score sind ausgeschlossen.

Beachten Sie, wie sensibel Felder sind password_hash Und private_notes erscheinen nicht in der Ausgabe. Dies macht die Funktion nützlich, um Daten vor der Protokollierung oder dem Senden an Frontend-Anwendungen zu bereinigen.

Sie können verschiedene Schemata für verschiedene Anwendungsfälle erstellen, z. B. ein Minimalschema für Listenansichten, ein detailliertes Schema für Einzelelementansichten und ein Admin-Schema, das alles umfasst.

# 5. Konvertieren von JSON in und aus der Punktnotation

Einige Systeme verwenden flache Schlüsselwertspeicher, Sie möchten jedoch mit verschachteltem JSON in Ihrem Code arbeiten. Dies lässt sich durch die Konvertierung zwischen flachen Punktnotationsschlüsseln und verschachtelten Strukturen erreichen.

Hier ist ein Funktionspaar für die bidirektionale Konvertierung.

// Konvertieren von JSON in Punktnotation

def json_to_dot_notation(information, parent_key=''):
    """
    Convert nested JSON to flat dot-notation dictionary.

    Args:
        information: Nested dictionary
        parent_key: Prefix for keys (utilized in recursion)

    Returns:
        Flat dictionary with dot-notation keys
    """
    gadgets = {}

    if isinstance(information, dict):
        for key, worth in information.gadgets():
            new_key = f"{parent_key}.{key}" if parent_key else key

            if isinstance(worth, dict):
                gadgets.replace(json_to_dot_notation(worth, new_key))
            else:
                gadgets(new_key) = worth
    else:
        gadgets(parent_key) = information

    return gadgets

// Konvertieren der Punktnotation in JSON

def dot_notation_to_json(flat_data):
    """
    Convert flat dot-notation dictionary to nested JSON.

    Args:
        flat_data: Dictionary with dot-notation keys

    Returns:
        Nested dictionary
    """
    end result = {}

    for key, worth in flat_data.gadgets():
        elements = key.cut up('.')
        present = end result

        for i, half in enumerate(elements(:-1)):
            if half not in present:
                present(half) = {}
            present = present(half)

        present(elements(-1)) = worth

    return end result

Testen wir die Spherical-Journey-Konvertierung:

import json
# Authentic nested JSON
config = {
    "app": {
        "title": "MyApp",
        "model": "1.0.0"
    },
    "database": {
        "host": "localhost",
        "credentials": {
            "username": "admin",
            "password": "secret"
        }
    },
    "options": {
        "analytics": True,
        "notifications": False
    }
}

# Convert to dot notation (for atmosphere variables)
flat = json_to_dot_notation(config)
print("Flat format:")
for key, worth in flat.gadgets():
    print(f"  {key} = {worth}")

print("n" + "="*50 + "n")

# Convert again to nested JSON
nested = dot_notation_to_json(flat)

print("Nested format:")
print(json.dumps(nested, indent=2))

Ausgabe:

Flat format:
  app.title = MyApp
  app.model = 1.0.0
  database.host = localhost
  database.credentials.username = admin
  database.credentials.password = secret
  options.analytics = True
  options.notifications = False

==================================================

Nested format:
{
  "app": {
    "title": "MyApp",
    "model": "1.0.0"
  },
  "database": {
    "host": "localhost",
    "credentials": {
      "username": "admin",
      "password": "secret"
    }
  },
  "options": {
    "analytics": true,
    "notifications": false
  }
}

Der json_to_dot_notation Die Funktion flacht die Struktur ab, indem sie rekursiv durch verschachtelte Wörterbücher geht und Schlüssel mit Punkten verbindet. Im Gegensatz zur früheren Flatten-Funktion verarbeitet diese keine Arrays; Es ist für Konfigurationsdaten optimiert, bei denen es sich ausschließlich um Schlüsselwerte handelt.

Der dot_notation_to_json Funktion kehrt den Prozess um. Es teilt jeden Schlüssel in Punkte auf und baut die verschachtelte Struktur auf, indem es nach Bedarf Zwischenwörterbücher erstellt. Die Schleife verarbeitet alle Teile außer dem letzten und erstellt Verschachtelungsebenen. Dann weist es den Wert dem letzten Schlüssel zu.

Durch diesen Ansatz bleibt Ihre Konfiguration lesbar und wartbar, während gleichzeitig die Einschränkungen flacher Schlüsselwertsysteme eingehalten werden.

# Zusammenfassung

Die JSON-Verarbeitung geht über das Grundlegende hinaus json.hundreds(). In den meisten Projekten benötigen Sie Werkzeuge zum Navigieren in verschachtelten Strukturen, zum Transformieren von Formen, zum Zusammenführen von Konfigurationen, zum Filtern von Feldern und zum Konvertieren zwischen Formaten.

Die Techniken in diesem Artikel lassen sich auch auf andere Datenverarbeitungsaufgaben übertragen. Sie können diese Muster für XML, YAML oder benutzerdefinierte Datenformate ändern.

Beginnen Sie mit der sicheren Zugriffsfunktion, um KeyError-Ausnahmen in Ihrem Code zu verhindern. Fügen Sie die anderen hinzu, wenn Sie auf spezifische Anforderungen stoßen. Viel Spaß beim Codieren!

Bala Priya C ist ein Entwickler und technischer Redakteur aus Indien. Sie arbeitet gerne an der Schnittstelle von Mathematik, Programmierung, Datenwissenschaft und Inhaltserstellung. Zu ihren Interessen- und Fachgebieten gehören DevOps, Datenwissenschaft und Verarbeitung natürlicher Sprache. Sie liebt es zu lesen, zu schreiben, zu programmieren und Kaffee zu trinken! Derzeit arbeitet sie daran, zu lernen und ihr Wissen mit der Entwickler-Group zu teilen, indem sie Tutorials, Anleitungen, Meinungsbeiträge und mehr verfasst. Bala erstellt außerdem ansprechende Ressourcenübersichten und Programmier-Tutorials.



Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert