جدول المحتويات

  • تاريخ موجز (جدًا) لـ JSON
  • انظروا ، إنه JSON!
  • لغة Python تدعم JSON أصلاً!
    القليل من المفردات
    تسلسل JSON
    مثال بسيط على التسلسل
    بعض حجج الكلمات الرئيسية المفيدة
    إلغاء تسلسل JSON
    مثال بسيط على إلغاء التسلسل
  • مثال من العالم الحقيقي (نوعًا ما)
  • ترميز وفك ترميز كائنات Python المخصصة
    تبسيط هياكل البيانات
    ترميز أنواع مخصصة
    أنواع مخصصة فك التشفير
  • كله تمام!

منذ نشأتها ، سرعان ما أصبحت JSON المعيار الفعلي لتبادل المعلومات. من المحتمل أنك هنا لأنك تحتاج إلى نقل بعض البيانات من هنا إلى هناك. من المحتمل أنك تجمع المعلومات من خلال واجهة برمجة التطبيقات أو تخزن بياناتك في قاعدة بيانات المستندات. بطريقة أو بأخرى ، ستصل إلى مستوى رقبتك في JSON ، ويجب عليك الخروج من Python.

لحسن الحظ ، هذه مهمة شائعة جدًا ، وكما هو الحال مع المهام الأكثر شيوعًا ، فإن بايثون تجعلها سهلة بشكل مثير للاشمئزاز. لا تخف يا رفاق بايثونير و بايثونيستاس. هذا سيكون نسيم!

إذن ، نستخدم JSON لتخزين البيانات وتبادلها؟ نعم ، لقد حصلت عليه! إنه ليس أكثر من تنسيق قياسي يستخدمه المجتمع لتمرير البيانات. ضع في اعتبارك أن JSON ليس التنسيق الوحيد المتاح لهذا النوع من العمل ، ولكن XML و YAML هما على الأرجح التنسيقان الآخران الوحيدان اللذان يستحق الذكر في نفس الوقت.

تاريخ موجز (جدًا) لـ JSON

ليس من المستغرب أن JavaScript Object Notation مستوحى من مجموعة فرعية من لغة برمجة JavaScript التي تتعامل مع بناء الجملة الحرفية للكائن. لديهم موقع أنيق يشرح الأمر برمته. لا داعي للقلق: لقد أصبحت JSON منذ فترة طويلة حيادية اللغة وهي موجودة كمعيارها الخاص ، لذلك يمكننا لحسن الحظ تجنب JavaScript من أجل هذه المناقشة.

في النهاية ، اعتمد المجتمع ككل JSON لأنه من السهل على كل من البشر والآلات الإنشاء والفهم.

انظروا ، إنه JSON!

إستعد. أنا على وشك أن أريكم بعض صور JSON الواقعية – تمامًا كما كنت سترى هناك في البرية. لا بأس: من المفترض أن تكون لغة JSON قابلة للقراءة من قبل أي شخص يستخدم لغة من النمط C ، و Python هي لغة من النمط C … لذلك هذا أنت!

{
    "firstName": "Jane",
    "lastName": "Doe",
    "hobbies": ["running", "sky diving", "singing"],
    "age": 35,
    "children": [
        {
            "firstName": "Alice",
            "age": 6
        },
        {
            "firstName": "Bob",
            "age": 8
        }
    ]
}

كما ترى ، يدعم JSON الأنواع الأولية ، مثل السلاسل والأرقام ، بالإضافة إلى القوائم والكائنات المتداخلة.

انتظر ، هذا يشبه قاموس بايثون! أنا أوافق؟ إنه رمز كائن عالمي إلى حد كبير في هذه المرحلة ، لكنني لا أعتقد أن UON يتدحرج على لسانه بشكل جيد تمامًا. لا تتردد في مناقشة البدائل في التعليقات.

يا للعجب! لقد نجوت من مواجهتك الأولى مع بعض JSON البرية. الآن تحتاج فقط إلى تعلم كيفية ترويضها.

لغة Python تدعم JSON أصلاً!

تأتي Python مع حزمة مدمجة تسمى json لتشفير وفك تشفير بيانات JSON.

فقط ارمي هذا الرجل الصغير في أعلى الملف الخاص بك:

import json

القليل من المفردات

عادةً ما تسمى عملية تشفير JSON التسلسل. يشير هذا المصطلح إلى تحويل البيانات إلى سلسلة من البايتات (ومن ثم التسلسلية) ليتم تخزينها أو نقلها عبر الشبكة. قد تسمع أيضًا مصطلح التنظيم ، ولكن هذه مناقشة أخرى كاملة. بطبيعة الحال ، فإن إلغاء التسلسل هو عملية متبادلة لفك تشفير البيانات التي تم تخزينها أو تسليمها في معيار JSON.

ييكيس! هذا يبدو تقنيًا جدًا. قطعا. لكن في الواقع ، كل ما نتحدث عنه هنا هو القراءة والكتابة. فكر في الأمر على هذا النحو: الترميز هو لكتابة البيانات على القرص ، بينما فك التشفير هو لقراءة البيانات في الذاكرة.

تسلسل JSON

ماذا يحدث بعد معالجة الكمبيوتر للكثير من المعلومات؟ يجب أن تأخذ تفريغ البيانات. وفقًا لذلك ، تعرض مكتبة json طريقة التفريغ () لكتابة البيانات إلى الملفات. هناك أيضًا طريقة dumps () (تُنطق باسم “dump-s”) للكتابة إلى سلسلة Python.

يتم ترجمة كائنات Python البسيطة إلى JSON وفقًا لتحويل بديهي إلى حد ما.
Python JSON
كائن ديكت
قائمة ، مجموعة الصفيف
سلسلة str
عدد صحيح ، طويل ، عائم
حقيقي حقيقي
خطأ خطأ
لا شيء فارغ

مثال بسيط على التسلسل

تخيل أنك تعمل مع كائن Python في الذاكرة يبدو قليلاً مثل هذا:

data = {
    "president": {
        "name": "Zaphod Beeblebrox",
        "species": "Betelgeusian"
    }
}

من الأهمية بمكان أن تقوم بحفظ هذه المعلومات على القرص ، لذا فإن مهمتك هي كتابتها في ملف.

باستخدام مدير سياق Python ، يمكنك إنشاء ملف يسمى data_file.json وفتحه في وضع الكتابة. (تنتهي ملفات JSON بسهولة بامتداد .json.)

with open("data_file.json", "w") as write_file:
    json.dump(data, write_file)

لاحظ أن dump () يأخذ وسيطين موضعيين: (1) كائن البيانات المراد تسلسله ، و (2) الكائن الشبيه بالملف الذي سيتم كتابة البايت عليه.

أو ، إذا كنت تميل إلى الاستمرار في استخدام بيانات JSON المتسلسلة هذه في برنامجك ، فيمكنك كتابتها إلى كائن Python str أصلي.

json_string = json.dumps(data)

لاحظ أن الكائن الذي يشبه الملف غائب نظرًا لأنك لا تكتب فعليًا على القرص. بخلاف ذلك ، فإن عمليات التفريغ () تشبه تمامًا التفريغ ().

الصيحة! لقد أنجبت طفلاً JSON ، وأنت على استعداد لإطلاقه في البرية لتنمو بشكل كبير وقوي.

بعض حجج الكلمات الرئيسية المفيدة

تذكر أنه من المفترض أن تكون لغة JSON سهلة القراءة من قِبل البشر ، إلا أن البنية القابلة للقراءة لا تكفي إذا تم دمجها معًا. بالإضافة إلى أنه من المحتمل أن يكون لديك أسلوب برمجة مختلف عني ، وقد يكون من الأسهل بالنسبة لك قراءة التعليمات البرمجية عندما يتم تنسيقها حسب رغبتك.

ملاحظة: كلا الأسلوبين dump () و dumps () تستخدمان نفس الوسيطات الأساسية.

الخيار الأول الذي يريد معظم الناس تغييره هو المسافة البيضاء. يمكنك استخدام وسيطة الكلمة الأساسية للمسافة البادئة لتحديد حجم المسافة البادئة للبنى المتداخلة. تحقق من الاختلاف بنفسك باستخدام البيانات ، التي حددناها أعلاه ، وتشغيل الأوامر التالية في وحدة التحكم:

>>> json.dumps(data)
>>> json.dumps(data, indent=4)

خيار تنسيق آخر هو وسيطة الكلمات الأساسية الفواصل. بشكل افتراضي ، هذه هي مجموعتان من السلاسل الفاصلة (“،”، “:”) ، ولكن البديل الشائع لـ JSON المضغوط هو (“،”، “:”). ألقِ نظرة على نموذج JSON مرة أخرى لترى أين تلعب هذه الفواصل.

هناك آخرون ، مثل sort_keys ، لكن ليس لدي أي فكرة عما يفعله هذا الشخص. يمكنك العثور على قائمة كاملة في المستندات إذا كنت مهتمًا.

إلغاء تسلسل JSON

رائع ، يبدو أنك قد التقطت لنفسك بعض JSON البري! حان الوقت الآن لإضفاء الشكل. في مكتبة json ، ستجد load () و load () لتحويل البيانات المشفرة JSON إلى كائنات Python.

تمامًا مثل التسلسل ، يوجد جدول تحويل بسيط لإلغاء التسلسل ، على الرغم من أنه يمكنك على الأرجح تخمين كيف يبدو بالفعل.
جسون بايثون
كائن ديكت
قائمة الصفيف
سلسلة سلسلة
عدد (كثافة العمليات)
عدد (حقيقي) عائم
حقيقي حقيقي
خطأ خطأ
لا شيء لا شيء

من الناحية الفنية ، لا يمثل هذا التحويل معكوسًا مثاليًا لجدول التسلسل. هذا يعني بشكل أساسي أنه إذا قمت بترميز كائن الآن ثم فك تشفيره مرة أخرى لاحقًا ، فقد لا تحصل على نفس الكائن مرة أخرى. أتخيل أنه يشبه إلى حد ما النقل الآني: قم بتكسير جزيئاتي هنا وإعادة تجميعها مرة أخرى هناك. هل ما زلت نفس الشخص؟

في الواقع ، ربما يكون الأمر أشبه بإقناع صديق بترجمة شيء ما إلى اليابانية وصديق آخر لترجمته مرة أخرى إلى اللغة الإنجليزية. بغض النظر ، فإن أبسط مثال هو ترميز tuple واستعادة قائمة بعد فك التشفير ، مثل:

>>> blackjack_hand = (8, "Q")
>>> encoded_hand = json.dumps(blackjack_hand)
>>> decoded_hand = json.loads(encoded_hand)

>>> blackjack_hand == decoded_hand
False
>>> type(blackjack_hand)
<class 'tuple'>
>>> type(decoded_hand)
<class 'list'>
>>> blackjack_hand == tuple(decoded_hand)
True

مثال بسيط على إلغاء التسلسل

هذه المرة ، تخيل أن لديك بعض البيانات المخزنة على القرص والتي ترغب في معالجتها في الذاكرة. ستظل تستخدم مدير السياق ، ولكن هذه المرة ستفتح ملف data_file.json الحالي في وضع القراءة.

with open("data_file.json", "r") as read_file:
    data = json.load(read_file)

الأمور واضحة ومباشرة هنا ، ولكن ضع في اعتبارك أن نتيجة هذه الطريقة يمكن أن ترجع أيًا من أنواع البيانات المسموح بها من جدول التحويل. هذا مهم فقط إذا كنت تقوم بتحميل بيانات لم تراها من قبل. في معظم الحالات ، سيكون الكائن الجذر عبارة عن إملاء أو قائمة.

إذا كنت قد سحبت بيانات JSON من برنامج آخر أو حصلت بطريقة أخرى على سلسلة من البيانات بتنسيق JSON في Python ، فيمكنك إلغاء تسلسل ذلك بسهولة باستخدام الأحمال () ، والتي يتم تحميلها بشكل طبيعي من سلسلة:

json_string = """
{
    "researcher": {
        "name": "Ford Prefect",
        "species": "Betelgeusian",
        "relatives": [
            {
                "name": "Zaphod Beeblebrox",
                "species": "Betelgeusian"
            }
        ]
    }
}
"""
data = json.loads(json_string)

هاهو! لقد قمت بترويض JSON البري ، وهو الآن تحت سيطرتك. لكن ما تفعله بهذه القوة متروك لك. يمكنك إطعامها ، ورعايتها ، وحتى تعليمها الحيل. ليس الأمر أنني لا أثق بك … لكني أبقيه مقيدًا ، حسنًا؟

مثال من العالم الحقيقي (نوعًا ما)

لمثالك التمهيدي ، ستستخدم JSONPlaceholder ، وهو مصدر رائع لبيانات JSON المزيفة لأغراض التدريب.

قم أولاً بإنشاء ملف نصي يسمى scratch.py ​​، أو أي شيء تريده. لا أستطيع حقًا إيقافك.

ستحتاج إلى تقديم طلب واجهة برمجة التطبيقات إلى خدمة JSONPlaceholder ، لذلك ما عليك سوى استخدام حزمة الطلبات للقيام بالأعباء الثقيلة. أضف هذه الواردات في الجزء العلوي من ملفك:

import json
import requests

الآن ، ستعمل مع قائمة TODOs مثل … كما تعلم ، إنها طقوس المرور أو أي شيء آخر.

انطلق وقم بتقديم طلب إلى JSONPlaceholder API لنقطة نهاية todos /. إذا لم تكن على دراية بالطلبات ، فهناك بالفعل طريقة json () مفيدة ستؤدي كل العمل نيابة عنك ، ولكن يمكنك التدرب على استخدام مكتبة json لإلغاء تسلسل سمة النص لكائن الاستجابة. يجب أن يبدو مثل هذا:

response = requests.get("https://jsonplaceholder.typicode.com/todos")
todos = json.loads(response.text)

لا تعتقد أن هذا يعمل؟ حسنًا ، قم بتشغيل الملف في الوضع التفاعلي واختبره بنفسك. أثناء تواجدك فيه ، تحقق من نوع المهام. إذا كنت تشعر بالمغامرة ، ألق نظرة خاطفة على أول 10 عناصر أو نحو ذلك في القائمة.

>>> todos == response.json()
True
>>> type(todos)
<class 'list'>
>>> todos[:10]
...

انظر ، لن أكذب عليك ، لكنني سعيد لأنك متشكك.

ما هو الوضع التفاعلي؟ آه ، أعتقد أنك لن تسأل أبدًا! هل تعرف كيف تقفز دائمًا ذهابًا وإيابًا بين المحرر والمحطة؟ حسنًا ، نحن مستخدمي Pythoneers الخادعين نستخدم العلامة التفاعلية -i عند تشغيل البرنامج النصي. هذه خدعة صغيرة رائعة لاختبار الكود لأنه يقوم بتشغيل البرنامج النصي ثم يفتح موجه أوامر تفاعلي مع إمكانية الوصول إلى جميع البيانات من البرنامج النصي!

حسنًا ، حان الوقت لبعض العمل. يمكنك رؤية بنية البيانات من خلال زيارة نقطة النهاية في المتصفح ، ولكن إليك نموذج TODO:

{
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
}

هناك عدة مستخدمين ، لكل منهم معرف مستخدم فريد ، ولكل مهمة خاصية مكتملة منطقية. هل يمكنك تحديد المستخدمين الذين أكملوا معظم المهام؟

# Map of userId to number of complete TODOs for that user
todos_by_user = {}

# Increment complete TODOs count for each user.
for todo in todos:
    if todo["completed"]:
        try:
            # Increment the existing user's count.
            todos_by_user[todo["userId"]] += 1
        except KeyError:
            # This user has not been seen. Set their count to 1.
            todos_by_user[todo["userId"]] = 1

# Create a sorted list of (userId, num_complete) pairs.
top_users = sorted(todos_by_user.items(), 
                   key=lambda x: x[1], reverse=True)

# Get the maximum number of complete TODOs.
max_complete = top_users[0][1]

# Create a list of all users who have completed
# the maximum number of TODOs.
users = []
for user, num_complete in top_users:
    if num_complete < max_complete:
        break
    users.append(str(user))

max_users = " and ".join(users)

نعم ، نعم ، التنفيذ أفضل ، ولكن النقطة المهمة هي أنه يمكنك الآن معالجة بيانات JSON ككائن Python عادي!

لا أعرف شيئًا عنك ، ولكن عندما أقوم بتشغيل البرنامج النصي بشكل تفاعلي مرة أخرى ، أحصل على النتائج التالية:

>>> s = "s" if len(users) > 1 else ""
>>> print(f"user{s} {max_users} completed {max_complete} TODOs")
users 5 and 10 completed 12 TODOs

هذا رائع وكل شيء ، لكنك هنا للتعرف على JSON. لمهمتك النهائية ، ستقوم بإنشاء ملف JSON يحتوي على TODOs المكتملة لكل من المستخدمين الذين أكملوا الحد الأقصى لعدد TODO.

كل ما عليك فعله هو تصفية المهام وكتابة القائمة الناتجة إلى ملف. من أجل الأصالة ، يمكنك استدعاء ملف الإخراج filtered_data_file.json. قد توجد طرق يمكنك اتباعها لحل هذا الأمر ، ولكن إليك إحدى الطرق:

# Define a function to filter out completed TODOs 
# of users with max completed TODOS.
def keep(todo):
    is_complete = todo["completed"]
    has_max_count = str(todo["userId"]) in users
    return is_complete and has_max_count

# Write filtered TODOs to file.
with open("filtered_data_file.json", "w") as data_file:
    filtered_todos = list(filter(keep, todos))
    json.dump(filtered_todos, data_file, indent=2)

رائع ، لقد تخلصت من جميع البيانات التي لا تحتاجها وحفظت الأشياء الجيدة في ملف جديد تمامًا! قم بتشغيل البرنامج النصي مرة أخرى وتحقق من filtered_data_file.json للتحقق من نجاح كل شيء. سيكون في نفس الدليل مثل scratch.py ​​عند تشغيله.

الآن بعد أن وصلت إلى هذا الحد ، أراهن أنك تشعر ببعض الأشياء الساخنة ، أليس كذلك؟ لا تكن مغرورًا: التواضع فضيلة. أنا أميل إلى الاتفاق معك رغم ذلك. حتى الآن ، كان الإبحار سلسًا ، ولكن قد ترغب في تثبيت الأبواب لهذه المرحلة الأخيرة من الرحلة.

ترميز وفك ترميز كائنات Python المخصصة

ماذا يحدث عندما نحاول إجراء تسلسل لفئة Elf من تطبيق Dungeons & Dragons الذي تعمل عليه؟

class Elf:
    def __init__(self, level, ability_scores=None):
        self.level = level
        self.ability_scores = {
            "str": 11, "dex": 12, "con": 10,
            "int": 16, "wis": 14, "cha": 13
        } if ability_scores is None else ability_scores
        self.hp = 10 + self.ability_scores["con"]

ليس من المستغرب أن تشتكي Python من أن Elf غير قابل للتسلسل (وهو ما ستعرفه إذا كنت قد حاولت إخبار Elf بخلاف ذلك):

>>> elf = Elf(level=4)
>>> json.dumps(elf)
TypeError: Object of type 'Elf' is not JSON serializable

على الرغم من أن وحدة json يمكنها التعامل مع معظم أنواع Python المضمنة ، إلا أنها لا تفهم كيفية تشفير أنواع البيانات المخصصة افتراضيًا. يبدو الأمر أشبه بمحاولة وضع مشبك مربّع في ثقب دائري — أنت بحاجة إلى منشار أزيز وإشراف أبوي.

تبسيط هياكل البيانات

الآن ، السؤال هو كيفية التعامل مع هياكل البيانات الأكثر تعقيدًا. حسنًا ، يمكنك محاولة ترميز JSON وفك تشفيره يدويًا ، ولكن هناك حل أكثر ذكاءً قليلاً سيوفر لك بعض العمل. بدلاً من الانتقال مباشرة من نوع البيانات المخصصة إلى JSON ، يمكنك طرح خطوة وسيطة.

كل ما عليك فعله هو تمثيل بياناتك من حيث الأنواع المضمنة التي يفهمها json بالفعل. بشكل أساسي ، تقوم بترجمة الكائن الأكثر تعقيدًا إلى تمثيل أبسط ، والذي تترجمه وحدة json بعد ذلك إلى JSON. إنها مثل خاصية متعدية في الرياضيات: إذا كانت A = B و B = C ، إذن A = C.

لفهم هذا الأمر ، ستحتاج إلى كائن معقد لتلعب به. يمكنك استخدام أي فئة مخصصة تريدها ، لكن لغة Python بها نوع مضمن يسمى معقد لتمثيل الأرقام المعقدة ، ولا يمكن إجراء تسلسل افتراضيًا. لذلك ، من أجل هذه الأمثلة ، سيكون كائنك المعقد كائنًا معقدًا. مرتبك حتى الآن؟

>>> z = 3 + 8j
>>> type(z)
<class 'complex'>
>>> json.dumps(z)
TypeError: Object of type 'complex' is not JSON serializable

من أين تأتي الأعداد المركبة؟ كما ترى ، عندما يحب عدد حقيقي ورقم وهمي بعضهما البعض كثيرًا ، فإنهما يجمعان معًا لإنتاج رقم يسمى (بشكل مبرر) معقد.

من الأسئلة الجيدة التي يجب أن تطرحها على نفسك عند العمل مع أنواع مخصصة ما هو الحد الأدنى من المعلومات اللازمة لإعادة إنشاء هذا الكائن؟ في حالة الأعداد المركبة ، ما عليك سوى معرفة الأجزاء الحقيقية والخيالية ، وكلاهما يمكنك الوصول إليه كسمات على الكائن المعقد:

>>> z.real
3.0
>>> z.imag
8.0

يعد تمرير نفس الأرقام إلى مُنشئ معقد كافيًا لإرضاء عامل المقارنة __eq__:

>>> complex(3, 8) == z
True

يعد تقسيم أنواع البيانات المخصصة إلى مكوناتها الأساسية أمرًا بالغ الأهمية لكل من عمليات التسلسل وإلغاء التسلسل.

ترميز أنواع مخصصة

لترجمة كائن مخصص إلى JSON ، كل ما عليك فعله هو توفير وظيفة تشفير للمعامل الافتراضي للطريقة dump (). ستستدعي وحدة json هذه الوظيفة على أي كائنات غير قابلة للتسلسل في الأصل. فيما يلي وظيفة فك تشفير بسيطة يمكنك استخدامها للتدريب:

def encode_complex(z):
    if isinstance(z, complex):
        return (z.real, z.imag)
    else:
        type_name = z.__class__.__name__
        raise TypeError(f"Object of type '{type_name}' is not JSON serializable")

لاحظ أنه من المتوقع أن تثير خطأ TypeError إذا لم تحصل على نوع الكائن الذي كنت تتوقعه. بهذه الطريقة ، تتجنب إجراء تسلسل غير مقصود لأي من الجان. الآن يمكنك محاولة ترميز العناصر المعقدة بنفسك!

>>> json.dumps(9 + 5j, default=encode_complex)
'[9.0, 5.0]'
>>> json.dumps(elf, default=encode_complex)
TypeError: Object of type 'Elf' is not JSON serializable

لماذا قمنا بترميز العدد المركب على هيئة tuple؟ سؤال رائع! لم يكن هذا بالتأكيد هو الخيار الوحيد ، كما أنه ليس بالضرورة الخيار الأفضل. في الواقع ، لن يكون هذا تمثيلًا جيدًا جدًا إذا أردت في أي وقت فك تشفير الكائن لاحقًا ، كما سترى قريبًا.

الطريقة الأخرى الشائعة هي تصنيف JSONEncoder القياسي وتجاوز طريقة () الافتراضية الخاصة به:

class ComplexEncoder(json.JSONEncoder):
    def default(self, z):
        if isinstance(z, complex):
            return (z.real, z.imag)
        else:
            return super().default(z)

بدلاً من رفع TypeError بنفسك ، يمكنك ببساطة السماح للفئة الأساسية بمعالجتها. يمكنك استخدام هذا إما مباشرة في طريقة dump () عبر المعامل cls أو عن طريق إنشاء مثيل من المشفر واستدعاء طريقة encode ():

>>> json.dumps(2 + 5j, cls=ComplexEncoder)
'[2.0, 5.0]'

>>> encoder = ComplexEncoder()
>>> encoder.encode(3 + 6j)
'[3.0, 6.0]'

أنواع مخصصة فك التشفير

في حين أن الأجزاء الحقيقية والخيالية للعدد المركب ضرورية للغاية ، إلا أنها في الواقع ليست كافية تمامًا لإعادة إنشاء الكائن. هذا ما يحدث عندما تحاول ترميز رقم معقد باستخدام ComplexEncoder ثم فك ترميز النتيجة:

>>> complex_json = json.dumps(4 + 17j, cls=ComplexEncoder)
>>> json.loads(complex_json)
[4.0, 17.0]

كل ما تحصل عليه هو قائمة ، وعليك تمرير القيم إلى مُنشئ معقد إذا أردت هذا الكائن المعقد مرة أخرى. أذكر مناقشتنا حول النقل الآني. ما ينقص هو البيانات الوصفية ، أو المعلومات حول نوع البيانات التي تقوم بتشفيرها.

أفترض أن السؤال الذي يجب أن تطرحه على نفسك حقًا هو ما هو الحد الأدنى من المعلومات الضرورية والكافية لإعادة إنشاء هذا الكائن؟

تتوقع وحدة json أن يتم التعبير عن جميع الأنواع المخصصة ككائنات في معيار JSON. للتنوع ، يمكنك إنشاء ملف JSON هذه المرة يسمى complex_data.json وإضافة الكائن التالي الذي يمثل رقمًا معقدًا:

{
    "__complex__": true,
    "real": 42,
    "imag": 36
}

رؤية الشيء الذكي؟ هذا المفتاح “__complex__” هو البيانات الوصفية التي تحدثنا عنها للتو. لا يهم حقًا ما هي القيمة المرتبطة. لجعل هذا الاختراق الصغير يعمل ، كل ما عليك فعله هو التحقق من وجود المفتاح:

def decode_complex(dct):
    if "__complex__" in dct:
        return complex(dct["real"], dct["imag"])
    return dct

إذا لم يكن “__complex__” موجودًا في القاموس ، يمكنك فقط إرجاع الكائن والسماح لوحدة فك الترميز الافتراضية بالتعامل معه.

في كل مرة تحاول طريقة load () تحليل كائن ما ، يتم منحك الفرصة للتوسط قبل أن تشغل وحدة فك التشفير الافتراضية طريقها مع البيانات. يمكنك القيام بذلك عن طريق تمرير وظيفة فك التشفير الخاصة بك إلى معلمة object_hook.

العب الآن نفس نوع اللعبة كما كان من قبل:

>>> with open("complex_data.json") as complex_data:
...     data = complex_data.read()
...     z = json.loads(data, object_hook=decode_complex)
... 
>>> type(z)
<class 'complex'>

في حين أن object_hook قد يبدو وكأنه المقابل للمعامل الافتراضي لطريقة dump () ، فإن القياس يبدأ وينتهي هناك.

هذا لا يعمل مع كائن واحد فقط. جرب وضع قائمة الأعداد المركبة هذه في complex_data.json وتشغيل البرنامج النصي مرة أخرى:

[
  {
    "__complex__":true,
    "real":42,
    "imag":36
  },
  {
    "__complex__":true,
    "real":64,
    "imag":11
  }
]

إذا سارت الأمور على ما يرام ، فستحصل على قائمة بالكائنات المعقدة:

>>> with open("complex_data.json") as complex_data:
...     data = complex_data.read()
...     numbers = json.loads(data, object_hook=decode_complex)
... 
>>> numbers
[(42+36j), (64+11j)]

يمكنك أيضًا تجربة التصنيف الفرعي JSONDecoder وتجاوز object_hook ، ولكن من الأفضل التمسك بالحل خفيف الوزن كلما أمكن ذلك.

كله تمام!

تهانينا ، يمكنك الآن استخدام القوة الجبارة لـ JSON لجميع احتياجات Python الشائنة.

في حين أن الأمثلة التي عملت معها هنا هي بالتأكيد مفتعلة ومفرطة في التبسيط ، فإنها توضح سير عمل يمكنك تطبيقه على مهام أكثر عمومية:

  1. قم باستيراد حزمة json.
  2. اقرأ البيانات مع load () أو loads ().
  3. معالجة البيانات.
  4. اكتب البيانات المعدلة مع تفريغ () أو تفريغ ().

يعتمد ما تفعله ببياناتك بمجرد تحميلها في الذاكرة على حالة الاستخدام الخاصة بك. بشكل عام ، سيكون هدفك هو جمع البيانات من المصدر ، واستخراج المعلومات المفيدة ، وتمرير هذه المعلومات أو الاحتفاظ بسجل لها.

لقد قمت اليوم برحلة: لقد التقطت بعض JSON البرية وقمت بترويضها ، وقمت بالعودة في الوقت المناسب لتناول العشاء! كمكافأة إضافية ، فإن تعلم حزمة json سيجعل تعلم المخلل والتنظيم أمرًا سريعًا.

حظًا سعيدًا في كل مساعيك البايثونية المستقبلية!