Jak przekonwertować JSON na obiekt Pythona

Jak przekonwertować JSON na obiekt Pythona

Pythona json biblioteka posiada wiele narzędzi do kodowania i dekodowania danych w formacie JSON. W szczególności json.load() Metoda dekoduje JSON odczytany jako plik, a metoda json.loads() dekodować JSON odczytany jako ciąg znaków. Ogólnie rzecz biorąc, podczas dekodowania plików JSON dane są konwertowane do słowników Pythona, ale można je przekonwertować na obiekt niestandardowy za pomocą parametru object_hook.

Załóżmy na przykład, że masz następujący obiekt JSON:

json_obj = """{
  "name" : "Felipe",
  "email" : "[email protected]",
  "age" : 29
}"""

oraz następna klasa:

class User():
  name : str
  email : str
  age : int
  def __init__(self, input):
      self.name = input.get("name")
      self.email = input.get("email")
      self.age = input.get("age")

Jeśli zadzwonimy json.loads() w User jak object_hook parametr, User.__init__() metoda zostanie wywołana z odpowiednim kodem JSON dict jako wejście.

import json

user = json.loads(json_obj, object_hook = User)
print(f"User {user.name}, age {user.age}, email {user.email}")
User Felipe, age 29, email [email protected]

Ale co, jeśli masz zagnieżdżony JSON?

json.loads() faktycznie dzwoni object_hook za każdym razem, gdy odczytuje w pełni uformowany obiekt JSON z łańcucha. Rozważ następujący kod JSON zwrócony z pliku Interfejs API losowego generatora użytkowników

json_obj = """{
            "gender": "male",
            "name": {
                "title": "Mr",
                "first": "Ian",
                "last": "Walters"
            },
            "location": {
                "street": {
                    "number": 3161,
                    "name": "Saddle Dr"
                },
                "city": "Bendigo",
                "state": "Western Australia",
                "country": "Australia",
                "postcode": 4285,
                "coordinates": {
                    "latitude": "-84.7903",
                    "longitude": "-29.1020"
                },
                "timezone": {
                    "offset": "+9:00",
                    "description": "Tokyo, Seoul, Osaka, Sapporo, Yakutsk"
                }
            },
            "email": "[email protected]",
            "login": {
                "uuid": "6ee5b2e8-01c3-4314-8f7f-80059f5dd9ec",
                "username": "lazyzebra585",
                "password": "walter",
                "salt": "afXmogsa",
                "md5": "a40e87023b57a4a60c7cb398584cbac3",
                "sha1": "74caf43400be38cce60a8da2e6d1c367246505c2",
                "sha256": "1becdf34bcc6704726c7e9b38821a5792f9dd0689d30789fb5e099a6e51e860a"
            },
            "dob": {
                "date": "1947-06-06T02:45:41.895Z",
                "age": 75
            },
            "registered": {
                "date": "2003-03-25T00:15:32.791Z",
                "age": 19
            },
            "phone": "06-9388-6976",
            "cell": "0469-101-424",
            "id": {
                "name": "TFN",
                "value": "561493929"
            },
            "picture": {
                "large": "https://randomuser.me/api/portraits/men/32.jpg",
                "medium": "https://randomuser.me/api/portraits/med/men/32.jpg",
                "thumbnail": "https://randomuser.me/api/portraits/thumb/men/32.jpg"
            },
            "nat": "AU"
        }"""

Wydrukujmy zdekodowany JSON na każdym kroku, aby zobaczyć, co się stanie:

json.loads(json_obj, object_hook = print)
{'title': 'Mr', 'first': 'Ian', 'last': 'Walters'}
{'number': 3161, 'name': 'Saddle Dr'}
{'latitude': '-84.7903', 'longitude': '-29.1020'}
{'offset': '+9:00', 'description': 'Tokyo, Seoul, Osaka, Sapporo, Yakutsk'}
{'street': None, 'city': 'Bendigo', 'state': 'Western Australia', 'country': 'Australia', 'postcode': 4285, 'coordinates': None, 'timezone': None}
{'uuid': '6ee5b2e8-01c3-4314-8f7f-80059f5dd9ec', 'username': 'lazyzebra585', 'password': 'walter', 'salt': 'afXmogsa', 'md5': 'a40e87023b57a4a60c7cb398584cbac3', 'sha1': '74caf43400be38cce60a8da2e6d1c367246505c2', 'sha256': '1becdf34bcc6704726c7e9b38821a5792f9dd0689d30789fb5e099a6e51e860a'}
{'date': '1947-06-06T02:45:41.895Z', 'age': 75}
{'date': '2003-03-25T00:15:32.791Z', 'age': 19}
{'name': 'TFN', 'value': '561493929'}
{'large': 'https://randomuser.me/api/portraits/men/32.jpg', 'medium': 'https://randomuser.me/api/portraits/med/men/32.jpg', 'thumbnail': 'https://randomuser.me/api/portraits/thumb/men/32.jpg'}
{'gender': 'male', 'name': None, 'location': None, 'email': '[email protected]', 'login': None, 'dob': None, 'registered': None, 'phone': '06-9388-6976', 'cell': '0469-101-424', 'id': None, 'picture': None, 'nat': 'AU'}

So json.loads() wzywa object_hook za każdym razem, gdy odczytuje w pełni uformowany kod JSON, czyli za każdym razem, gdy zamyka parę nawiasów {}. Następnie tworzy cały obiekt JSON, używając wyniku metody object_hook funkcja – uwaga None (wartość zwracana przez print) w ostatnim wydrukowanym wierszu.

Pokażemy dwa obejścia tego problemu. Pierwszym z nich jest modyfikacja naszego User.__init__() metoda, aby była bardziej elastyczna w odniesieniu do danych wejściowych. Zrobimy to za pomocą __dict__ atrybut. Każdy obiekt Pythona ma plik __dict__ atrybut, który przechowuje nazwę i wartość każdego atrybutu. Nasz zmodyfikowany __init__() metoda zaktualizuje ten słownik:

class User():
  def __init__(self, input):
      self.__dict__.update(input)

user = json.loads(json_obj, object_hook = User)
print(f"User {user.name.first} {user.name.last}, age {user.dob.age}, email {user.email}")

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

User Ian Walters, age 75, email [email protected]

Innym możliwym obejściem jest użycie collections.namedtuple klasa:

from collections import namedtuple

def create_user(input):
    User = namedtuple('User', input.keys())
    return User(**input)
    
user = json.loads(json_obj, object_hook=create_user)
print(f"User {user.name.first} {user.name.last}, age {user.dob.age}, email {user.email}")
User Ian Walters, age 75, email [email protected]

gdzie namedtuple('User', input.keys()) tworzy podklasę krotki o nazwie User z kluczami wejściowymi jako nazwami atrybutów i User(**input) przypisuje odpowiednie wartości atrybutom.

Znak czasu:

Więcej z Nadużycie stosu