Наш сетевой протокол обычно преобразует данные в 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
Женьшень на самом деле сталdata
dict после преобразования поля ({"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
так же.
Суммировать
Хоть мои потребности и удовлетворены, но в душе все равно остался большой знак вопроса, почему так задумано, подходящего ответа в интернете я не нашел, а в остальном нужно изучать анализ исходного кода.