25 Апр 2022
8 мин
30941

Как работать с модулем JSON в Python

JSON часто применяют, когда разрабатывают API и веб-приложения.

Содержание

Основной принцип работы интернета — обмен данными. Они бывают разных видов, например файл, строка или число. Есть структура данных, которая позволяет быстро и просто воссоздавать объекты и обмениваться этими данными по сети, — JSON.

JSON в Python

JSON — это строка со словарем. Она представлена в виде байтовой последовательности. Вы можете отправить ее по сети приложению, а в нём воссоздать полученную структуру в объекты языка.

💡 Пример JSON:

{
    "kwarg1": "value_1",
    "kwarg2": "value_2",
    "kwarg3": "value_3",
    "additional": ["value_4", "value_5", "value_6", ]
}

Сериализация и десериализация

В Python есть множество библиотек, чтобы работать с JSON, но мы рассмотрим встроенную библиотеку JSON Python. Она позволяет приводить любые структуры данных к JSON-объекту — вплоть до пользовательских классов. А из него получать совместимую для работы в Python сущность — объект языка.

Полностью разобраться в Python вы сможете на курсе «Python-разработчик». Преподаватели объяснят все нюансы доступным языком, а вы выполните практические задания. Например, разработаете виджет банковских операций. После обучения у вас будет готовое портфолио и диплом о профессиональной переподготовке.

Упаковка объектов в байтовую последовательность называется сериализацией. А распаковка байтов в объекты языка программирования, приведение последовательности назад к типам и структурам, — десериализацией.

В байты данные необходимо переводить, чтобы отправлять их по сети или локально другому приложению, так как иной формат передать невозможно. Вот так преобразовывают данные из объектов Python в JSON и обратно:

>>> # импортируем библиотеку
>>> import json
>>> 
>>> # объявляем переменные
>>> string = "Some test string"
>>> integer = 211
>>> array = [1, 2, 3, 4, 5]
>>> 
>>> # создаем словарь
>>> mydict = {"title": string, "code": integer, "data": array}
>>> 
>>> # сериализуем его в JSON-структуру, как строку
>>> x = json.dumps(mydict)
>>> x
'{"title": "Some test string", "code": 211, "data": [1, 2, 3, 4, 5]}'
>>> 
>>> # проводим десериализацию JSON-объекта
>>> y = json.loads(x)
>>> y
{'title': 'Some test string', 'code': 211, 'data': [1, 2, 3, 4, 5]}
>>> 
>>> y["title"]
'Some test string'
>>> 

Функции

Dumps позволяет создать JSON-строку из переданного в нее объекта. Loads — преобразовать строку назад в объекты языка.

Dump и load используют, чтобы сохранить результат в файл или воссоздать объект. Работают они схожим образом, но требуют передачи специального объекта для работы с файлом — filehandler.

>>> import json # импортируем библиотеку
>>> 
>>> # создаем filehandler с помощью контекстного менеджера
>>> with open("data.json", "w") as fh:
...     json.dump([1, 2, 3, 4, 5], fh) # записываем структуру в файл
... 
>>> 
>>> # открываем тот же файл, но уже на чтение
>>> with open("data.json", "r") as fh:
...     json.load(fh) # загружаем структуру из файла
... 
[1, 2, 3, 4, 5]
>>> 

Как работать с пользовательскими объектами

Пользовательские классы не относятся к JSON-сериализуемым. Это значит, что просто применить к ним функции dumps, loads или dump и load не получится:

>>> # создаем пользовательский класс
>>> class Test:
...     def __init__(self, title, body):
...         self.title = title
...         self.body = body
... 
>>> # создаем экземпляр класса
>>> t = Test("Some string", "Here is a bit more text, but still isn't enough")
>>> 
>>> # пытаемся сериализовать его в JSON, но...
>>> json.dumps(t)
>>> # получаем ошибку TypeError, что класс несериализуем
>>> 

Решить эту проблему можно тремя способами.

🚀 Написать функцию

Чтобы сериализовать пользовательский объект в JSON-структуру данных, нужен аргумент default. Указывайте вызываемый объект, то есть функцию или статический метод.

Чтобы получить аргументы класса с их значениями, нужна встроенная функция __dict__, потому что любой класс — это словарь со ссылками на значения по ключу.

Чтобы сериализовать аргументы класса и их значения в JSON, напишите функцию:

>>> # используем анонимную функцию (лямбду), которая
>>> # в качестве сериализуемых данных указывает полученный __dict__ объекта
>>> json.dumps(t, default=lambda x: x.__dict__)
'{"title": "Some string", "body": "Here is a bit more text, but still isn\'t enough"}'
>>> 

Но можно создать отдельную функцию и указать ее в качестве аргумента:

>>> def to_json(obj):
...     if isinstance(obj, Test):
...         result = obj.__dict__
...         result["className"] = obj.__class__.__name__
...         return result
... 
>>> json.dumps(t, default=to_json)
'{"title": "Some string", "body": "Here is a bit more text, but still isn\'t enough", "className": "Test"}'
>>> 

❗ Мы добавили название класса в получаемую структуру. Такой подход позволяет безошибочно понять: сущность какого класса нужно десериализовать в объект.

Более подробно писать функции вы научитесь на курсе «Python-разработчик». А еще сами сделаете сервис проверки файлов с возможностью регистрации, отправки уведомлений и формирования отчета о задачах.

🚀 Создать расширение классов

Такого же результата добьетесь, если примените расширения специальных классов библиотеки:

>>> class TestEncoder(json.JSONEncoder):
...     def default(self, o):
...         return {"TITLE": o.title, "BODY": o.body, "CLASSNAME": o.__class__.__name__}
... 
>>> x = json.dumps(t, cls=TestEncoder)
>>> x
'{"TITLE": "Some string", "BODY": "Here is a bit more text, but still isn\'t enough"}'
>>> 
>>> y = json.loads(x)
>>> y
{'TITLE': 'Some string', 'BODY': "Here is a bit more text, but still isn't enough", 'CLASSNAME': 'Test'}
>>> 
>>> y["TITLE"]
'Some string'
>>> 

Такой подход можно использовать и в том случае, если класс состоит из нескольких других.

🚀 Применить паттерн «Адаптер»

Идея в том, чтобы написать класс, который приводит к JSON пользовательские объекты и восстанавливает их. Определите класс фигуры, формы и цвета:

class Figure:
    def __init__(self, title, form, color):
        self.title = title
        self.form = form
        self.color = color

    def __str__(self):
        return f"Figure: {self.title}, {repr(self.form)}, {repr(self.color)}"

class Form:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"<Form: {self.name}>"

class Color:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return f"<Color: {self.name}>"

Напишите класс, который будет приводить объекты фигуры к JSON, а из JSON воссоздавать полученный объект:

class JSONDataAdapter:
    @staticmethod
    def to_json(o):
        if isinstance(o, Figure):
            return json.dumps({
              "title": o.title,
              "form": o.form.name,
              "color": o.color.name,
            })

    @staticmethod
    def from_json(o):
        o = json.loads(o)

        try:
            form = Form(o["form"])
            color = Color(o["color"])
            figure = Figure(o["title"], form, color)
            return figure
        except AttributeError:
            print("Неверная структура")

Протестируйте решение:

if __name__ == '__main__':
    # создадим несколько цветов
    black = Color("Black")
    yellow = Color("Yellow")
    green = Color("Green")

    # несколько форм
    rountt = Form("Rounded")
    square = Form("Squared")

    # объекты фигур
    figure_one = Figure("Black Square", form=square, color=black)
    figure_two = Figure("Yellow Circle", form=rountt, color=yellow)

    print(“Отображение объектов”)
    print(figure_one)
    print(figure_two)
    print()

    # преобразуем данные в JSON
    jone = JSONDataAdapter.to_json(figure_one)
    jtwo = JSONDataAdapter.to_json(figure_two)

    print(“Отображение JSON”)
    print(jone)
    print(jtwo)
    print()

    # восстановим объекты
    restored_one = JSONDataAdapter.from_json(jone)
    restored_two = JSONDataAdapter.from_json(jtwo)

    print(“Отображение восстановленных объектов”)
    print(restored_one)
    print(restored_two)

Вывод терминала:

Отображение объектов

Figure: Black Square, <Form: Squared>, <Color: Black>
Figure: Yellow Circle, <Form: Rounded>, <Color: Yellow>

Отображение JSON

{"title": "Black Square", "form": "Squared", "color": "Black"}
{"title": "Yellow Circle", "form": "Rounded", "color": "Yellow"}

Отображение восстановленных объектов

{Figure: Black Square, <Form: Squared>, <Color: Black>
Figure: Yellow Circle, <Form: Rounded>, <Color: Yellow>

Главное о работе с JSON в Python

  • JSON — это стандарт обмена данными. Он позволяет легко сериализовать и десериализовать объекты.
  • Стандарт часто применяют, когда разрабатывают API и веб-приложения.
  • Для сериализации и десериализации объектов в строку или из строки используйте функции json.dumps/json.loads. Из файлов — json.dump/json.load.
  • Сериализовать можно любую пользовательскую структуру. Для этого создайте функцию, напишите расширение классов JSONEncoder/JSONDecoder или свою реализацию «Адаптера».

Изучайте Python на онлайн-курсе от Skypro «Python-разработчик». Программа рассчитана на новичков без опыта программирования и технического образования. Курс проходит в формате записанных коротких видеолекций. Будет много проверочных заданий и мастер-классов. В конце каждой недели — живая встреча с экспертами разработки для ответов на вопросы и разбора домашек.

Содержание

Добавить комментарий

Определи профессию по рисунку