Python реализует объект класса десериализации JSON

Python JSON

Наш сетевой протокол обычно преобразует данные в JSON, а затем передает их. Раньше в Java были реализованы сериализация и десериализация, будь тоjackson,все ещеfastjsonочень просты. Сейчас есть проекты, которые нужно разрабатывать на Python, и естественно надеяться, что такое удобство можно отразить и в Python.

Но после прочтения некоторых руководств в Интернете, когда дело доходит до десериализации, он в основном преобразуется вdictилиarray. Такой способ программирования для меня эмоционально неприемлем. Может быть, ни одна из этих библиотек JSON не поддерживает десериализацию в объекты класса? Я сразу отбросил эту идею, Python настолько мощный скриптовый язык, что без полноценной библиотеки JSON он невозможен.

Поэтому я исследовал оригиналjsonи сторонниеdemjsonиsimplejson.

1. Родной json

Я присмотрелся к родномуjsonизloadsопределение метода

def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

здесьobject_hookиobject_pairs_hookПараметры привлекли мое внимание, поэтому позвольте мне сосредоточиться наobject_hook.

В официальной документации указано следующее:

object_hook is an optional function that will be called with the result of any object literal decoded (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).

этоobject_hookСогласно объяснению документа, это пользовательская функция декодирования, dict после стандартной десериализации входного параметра может быть преобразован и выведен в нужный формат в соответствии с нашими собственными правилами.

я искал сноваobject_hook, каждый способ справиться с этой вещью состоит в том, чтобы использовать статический метод для преобразования dict в объект.

Наша структура данных такая

{"status":1,"info":"发布成功","data":{"id":"52","feed_id":"70"}}

Поэтому я написал этот код:

class Response:

    def __init__(self, status, info, data) -> None:
        super().__init__()
        self.status = status
        self.info = info
        self.data = data

    @staticmethod
    def object_hook(d):
        return Response(d['status'], d['info'], d['data'])
 ...
resp = json.loads(body, object_hook=Response.object_hook)

Поначалу проблем действительно не было, хотя пользоваться им было не так удобно, как json-библиотекой java, но все же он удовлетворял требованиям.

Хорошие времена не долгие, в данных, возвращаемых первым протестированным интерфейсом,dataЯвляется ли поле строкой, десериализация работает нормально. Но позже, когда интерфейс вернет структуруdataКогда поле представляет собой структуру dict,object_hookЖеньшень на самом деле сталdatadict после преобразования поля ({"id":"52","feed_id":"70"}) вместо полных данных.

Они ошеломлены, и я искал в Интернете, чтобы найти никакого вывода. Ну наконец-то я честно вернулся к официальной документации,read the fucking official document.

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

>>> class JSONObject:
...     def __init__(self, d):
...         self.__dict__ = d
...
>>>
>>> data = json.loads(s, object_hook=JSONObject)
>>> data.name
'ACME'
>>> data.shares
50
>>> data.price
490.1
>>>

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

Выше приведена официальная реализация библиотеки json, а как насчет двух других известных сторонних библиотек?

2. Демжсон

demjsonтакже поддерживаетhook. Есть два способа настройки:decodeконфигурация функций иset_hookКонфигурация функций

1. decode

def decode( txt, encoding=None, **kwargs )

decodeФункции могут указывать множество параметров, в том числеhookфункция.hookФункции задаются с помощью пар ключ-значение, где ключиhookИмя функции, значениеhookфункция.

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

  • decode_number
  • decode_float
  • decode_object
  • decode_array
  • decode_string
  • encode_value
  • encode_dict
  • encode_dict_key
  • encode_sequence
  • encode_bytes
  • encode_default
demjson.decode(body, encode='utf-8',decode_obbject=Reponse.object_hook)

Результат не сделал меня очень открытым, все еще неспособным иметь дело с вложенными структурами. Журнал показывает следующее:

2018-01-30 16:01:17,137 poster.py post_all 73 INFO    : {"status":1,"info":"\u53d1\u5e03\u6210\u529f","data":{"id":"54","feed_id":"72"}}
2018-01-30 16:01:17,138 response.py object_hook 13 INFO    : {'id': '54', 'feed_id': '72'}
2018-01-30 16:01:17,138 response.py object_hook 13 INFO    : {'status': 1, 'info': '发布成功', 'data': demjson.undefined}

достаточно странноobject_hookФункция вызывается дважды, первый разdataсодержимое поля, второе - полное содержимое, ноdataПоле не анализируется. Это очень странно, и это трудно понять! ! !

2. set_hook

set_hookфункция с вышеперечисленнымdecodeфункция не та,JSONфункции-члены класса, в то время какdecodeФункция — это статическая функция.

def set_hook(self, hookname, function)

Извлекая уроки из предыдущих уроков, в этот раз я внимательно прочитал документацию demjson и кое-что нашел.

Netsted values. When decoding JSON that has nested objects or arrays, the decoding hooks will be called once for every corresponding value, even if nested. Generally the decoding hooks will be called from the inner-most value outward, and then left to right.

Основное внимание здесь уделяется проблеме вложенности, когда происходит вложенность, каждый соответствующий тип будет вызыватьhookфункционируют один раз, а из самого внутреннего слоя — слева направо. Ну, я понимаю все проблемы, которые были раньше, получается, что это правило виновато, но я все еще не понимаю, почему оно так устроено.

set_hookкак пользоваться

    j = demjson.JSON()
    j.set_hook( 'decode_array', my_sort_array )
    j.decode(body, encode='utf-8')

Три, простой

Так много было сказано прежде,simplejsonПро способ и говорить нечего, с официалкойjsonбиблиотекаhookтак же.

Суммировать

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