כיצד להמיר JSON ל-Python Object

כיצד להמיר JSON ל-Python Object

פיתון json לספרייה יש כלי עזר רבים לקידוד ופענוח נתונים בפורמט JSON. בפרט, ה json.load() השיטה מפענחת JSON שנקרא כקובץ, וה- json.loads() לפענח JSON שנקרא כמחרוזת. באופן כללי, בעת פענוח קבצי JSON, הנתונים מומרים למילוני Python, אך ניתן להמיר אותם לאובייקט מותאם אישית באמצעות הפרמטר object_hook.

לדוגמה, נניח שיש לך את האובייקט JSON הבא:

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

והכיתה הבאה:

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")

אם נתקשר json.loads() עם User כמו object_hook פרמטר, ה User.__init__() השיטה תיקרא עם ה-JSON המקביל dict כקלט.

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]

אבל מה אם יש לך JSON מקונן?

json.loads() למעשה מכנה את object_hook פונקציה בכל פעם שהוא קורא אובייקט JSON שנוצר במלואו מהמחרוזת. שקול את ה-JSON הבא, שהוחזר מה- ממשק API של מחולל משתמשים אקראי

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"
        }"""

בואו נדפיס את ה-JSON המפוענח בכל שלב כדי לראות מה קורה:

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() קורא ל- object_hook מתפקד בכל פעם שהוא קורא JSON שנוצר במלואו, כלומר, בכל פעם שהוא סוגר זוג סוגר {}. לאחר מכן, הוא יוצר את כל אובייקט ה-JSON באמצעות התוצאה של ה- object_hook פונקציה - שימו לב ל None (ערך ההחזר של print) בשורה המודפסת האחרונה.

נציג שני דרכים לעקיפת הבעיה לבעיה זו. הראשון הוא לשנות את שלנו User.__init__() שיטה להיות גמישה יותר ביחס לקלט. נעשה זאת באמצעות ה __dict__ תְכוּנָה. לכל אובייקט Python יש א __dict__ תכונה שמחזיקה את השם והערך של כל תכונה. שונה שלנו __init__() השיטה תעדכן את המילון הזה:

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}")

עיין במדריך המעשי והמעשי שלנו ללימוד Git, עם שיטות עבודה מומלצות, סטנדרטים מקובלים בתעשייה ודף רמאות כלול. תפסיק לגוגל פקודות Git ולמעשה ללמוד זה!

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

דרך אפשרית נוספת היא להשתמש ב- collections.namedtuple מעמד:

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]

איפה namedtuple('User', input.keys()) יוצר תת-מחלקה tuple בשם User עם מקשי הקלט בתור שמות תכונות, ו User(**input) מקצה את הערכים המתאימים לתכונות.

בול זמן:

עוד מ Stackabuse