Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как сделать класс JSON сериализуемым

Автор вопроса: SergeyОтветов: 12

Как сделать класс Python сериализуемым?

Простой класс:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

Что мне делать, чтобы получить вывод:

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

Без ошибки

  • К сожалению, все ответы, кажется, отвечают на вопрос, как сериализовать класс? вместо вопроса о действии Как мне сделать класс сериализуемым? Эти ответы предполагают, что вы сами выполняете сериализацию, а не передаете объект какому-либо другому модулю, который его сериализует. Kyle Delaney 18.10.2019
  • Если вы используете Python3.5 +, вы можете использовать jsons. Он преобразует ваш объект (и все его атрибуты рекурсивно) в dict. import jsons см. Ответ ниже - он отлично работает tswaehn 02.04.2020
  • @KyleDelaney Я действительно надеялся на интерфейс / магический метод, который я мог бы реализовать, чтобы он стал доступным для поиска. Я предполагаю, что мне придется реализовать функцию .to_dict() или что-то, что может быть вызвано для объекта, прежде чем он будет передан модулю, который пытается его сериализовать. Felix B. 01.09.2020
  • см. stackoverflow.com/a/63718624/1497139 для начала работы с примесью JSONAble Wolfgang Fahl 03.09.2020
  • @FelixB. Вы можете использовать встроенную функцию vars в сочетании с json.dumps (см. Мой ответ stackoverflow.com/a/64469761/1587520 ) user1587520 23.11.2020

pythonjsonserialization

Ответы

Ответ 1 · рейтинг 637 · Manoj Govindan

У вас есть представление об ожидаемом результате? Например, подойдет ли это?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

В этом случае вы можете просто позвонить json.dumps(f.__dict__).

Если вам нужен более индивидуальный вывод, вам придется создать подкласс JSONEncoder и реализовать ваша собственная настраиваемая сериализация.

Тривиальный пример см. Ниже.

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

Затем вы передаете этот класс в метод json.dumps() как cls kwarg:

json.dumps(cls=MyEncoder)

Если вы также хотите декодировать, вам нужно будет указать пользовательский object_hook в JSONDecoder класс. Например:

>>> def from_json(json_object):
        if 'fname' in json_object:
            return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>

Ответ 2 · рейтинг 729 · Onur Yıldırım

Вот простое решение для простой функции:

.toJSON() Метод

Вместо сериализуемого класса JSON реализуйте метод сериализатора:

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__,
            sort_keys=True, indent=4)

Итак, вы просто вызываете его для сериализации:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

выведет:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

Ответ 3 · рейтинг 203 · gecco

Для более сложных классов вы можете использовать инструмент jsonpickle:

jsonpickle - это библиотека Python для сериализации и десериализации сложных объектов Python в JSON и обратно.

Стандартные библиотеки Python для кодирования Python в JSON, такие как json из stdlib, simplejson и demjson, могут обрабатывать только примитивы Python, которые имеют прямой эквивалент JSON (например, dicts, списки, строки, int и т. Д.). jsonpickle строится поверх этих библиотек и позволяет сериализовать более сложные структуры данных в JSON. jsonpickle обладает широкими возможностями настройки и расширения, что позволяет пользователю выбирать серверную часть JSON и добавлять дополнительные серверные части.

(ссылка на jsonpickle в PyPi)

Ответ 4 · рейтинг 135 · andyhasit

Большинство ответов связано с изменением вызова на json.dumps (), что не всегда возможно или желательно (например, это может произойти внутри компонента фреймворка).

Если вы хотите иметь возможность вызывать json.dumps (obj) как есть, тогда простое решение наследуется от dict:

class FileItem(dict):
    def __init__(self, fname):
        dict.__init__(self, fname=fname)

f = FileItem('tasks.txt')
json.dumps(f)  #No need to change anything here

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

Ответ 5 · рейтинг 59 · Jason S

Мне нравится ответ Онура, но я бы расширил его, включив дополнительный метод toJSON() для сериализации объектов:

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

Ответ 6 · рейтинг 46 · Fancy John

Просто добавьте метод to_json в свой класс следующим образом:

def to_json(self):
  return self.message # or how you want it to be serialized

И добавьте этот код (из этого ответа) где-нибудь в начало всего:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

Это обезьяна исправляет json-модуль при его импорте, поэтому JSONEncoder.default () автоматически проверяет наличие специального метода to_json () и использует его для кодирования объекта, если он найден.

Как и сказал Онур, но на этот раз вам не нужно обновлять все json.dumps() в вашем проекте.

Ответ 7 · рейтинг 42 · Paulo Freitas

Другой вариант - обернуть дамп JSON в отдельный класс:

import json

class FileItem:
    def __init__(self, fname):
        self.fname = fname

    def __repr__(self):
        return json.dumps(self.__dict__)

Или, что еще лучше, создать подкласс класса FileItem из класса JsonSerializable:

import json

class JsonSerializable(object):
    def toJson(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.toJson()


class FileItem(JsonSerializable):
    def __init__(self, fname):
        self.fname = fname

Тестирование:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

Ответ 8 · рейтинг 30 · user1587520

Как упоминалось во многих других ответах, вы можете передать функцию json.dumps для преобразования объектов, которые не являются одним из типов, поддерживаемых по умолчанию, в поддерживаемый тип. Удивительно, но ни один из них не упоминает простейший случай - использование встроенной функции vars для преобразования объектов в словарь, содержащий все их атрибуты:

json.dumps(obj, default=vars)

Обратите внимание, что это охватывает только базовые случаи, если вам нужна более конкретная сериализация для определенных типов (например, исключение определенных атрибутов или для объектов, у которых нет атрибута __dict__), вам необходимо использовать настраиваемую функцию или JSONEncoder, как описано в других ответах .

Ответ 9 · рейтинг 29 · tobigue

На днях я столкнулся с этой проблемой и реализовал более общую версию кодировщика для объектов Python, который может обрабатывать вложенные объекты и унаследованные поля:

import json
import inspect

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

Пример:

class C(object):
    c = "NO"
    def to_json(self):
        return {"c": "YES"}

class B(object):
    b = "B"
    i = "I"
    def __init__(self, y):
        self.y = y

    def f(self):
        print "f"

class A(B):
    a = "A"
    def __init__(self):
        self.b = [{"ab": B("y")}]
        self.c = C()

print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)

Результат:

{
  "a": "A",
  "b": [
    {
      "ab": {
        "b": "B",
        "i": "I",
        "y": "y"
      }
    }
  ],
  "c": {
    "c": "YES"
  },
  "i": "I"
}

Ответ 10 · рейтинг 29 · R H

Если вы используете Python3.5 +, вы можете использовать jsons. (PyPi: https://pypi.org/project/jsons/) Он преобразует ваш объект (и все его атрибуты рекурсивно) в dict.

import jsons

a_dict = jsons.dump(your_object)

Или, если вам нужна строка:

a_str = jsons.dumps(your_object)

Или, если ваш класс реализовал jsons.JsonSerializable:

a_dict = your_object.json

Ответ 11 · рейтинг 12 · tryer3000

import simplejson

class User(object):
    def __init__(self, name, mail):
        self.name = name
        self.mail = mail

    def _asdict(self):
        return self.__dict__

print(simplejson.dumps(User('alice', '[email protected]')))

при использовании стандартного json необходимо определить функцию default

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', '[email protected]'), default=default))

Ответ 12 · рейтинг 8 · ribamar

json ограничен с точки зрения объектов, которые он может напечатать, а jsonpickle (вам может понадобиться pip install jsonpickle) ограничен с точки зрения того, что он не может отступать от текста. Если вы хотите проверить содержимое объекта, класс которого вы не можете изменить, я все равно не смог бы найти более простой способ, чем:

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

Примечание: они по-прежнему не могут распечатать методы объекта.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы