Записки программиста, обо всем и ни о чем. Но, наверное, больше профессионального.

2013-04-29

Python Decimal & json.dumps/loads

В ходе работы над Mapfeatureserver наступил на грабли. Не работает сериализация в JSON, говорит, типа
File "c:\d\Python27\lib\json\encoder.py", line 178, in default
TypeError: Decimal('0E-11') is not JSON serializable

После разборок выяснилось, что проблем аж две: стандартная библиотека json не умеет работать с типом данных decimal.Decimal; в тестовой БД поля типа numeric (из которых и появляется питонячий Decimal) содержат «0.0» вместо NULL.
Еще неизвестно, какая проблема тяжелее. Замена null-ов на 0 произошла где-то в цепочке загрузки данных Esri gdb → shp → PostGIS. Скорее всего при выгрузке в шейп. Это означает, что в перспективе надо писать загрузчик данных напрямую из Esri gdb сразу в PostGIS. Узелок на память.

Проблема с сериализацией Decimal в JSON тоже не из легких.
Есть модуль simplejson, который понимает Decimal и ловко с ним работает.
Например так:
    data = {'a': 'b', 'c': decimal.Decimal(3.14), 'd': decimal.Decimal('0E-11')}
    text = simplejson.dumps(data, ensure_ascii=False, sort_keys=True, indent=2, use_decimal=True)
    print text.encode(CP)
что дает такой результат :
{
  "a": "b",
  "c": 3.140000000000000124344978758017532527446746826171875,
  "d": 0E-11
}
Или так:
    data = {'a': 'b', 'c': decimal.Decimal(3.14), 'd': decimal.Decimal('0E-11')}
    def jsonify(obj):
        if isinstance(obj, decimal.Decimal):
            return float(obj)
        raise TypeError(repr(obj) + " is not JSON serializable")
    text = simplejson.dumps(data, ensure_ascii=False, sort_keys=True, indent=2, default=jsonify, use_decimal=False)
    print text.encode(CP)
что дает несколько другой результат:
{
  "a": "b",
  "c": 3.14,
  "d": 0.0
}

В принципе работает, а вот в кожухе фигня получается. Почему? Потому, что это выходит не сериализация, в том смысле, что она необратима. При применении опции «use_decimal=True» все числа с плавающей точкой будут загружены (функция simplejson.loads) как Decimal. Если же «use_decimal=False», есть шанс, что некоторые числа вообще не загрузятся а если загрузятся, то с потерей точности и их тип будет НЕ Decimal. А если сделать свой сериализатор для типа Decimal, то на выходе dumps будет не совсем JSON. А все почему? Потому что числа с плавающей точкой представлены двумя типами, в то время как их запись текстом одна и та же: «xyz.abc».

Что же делать? Зависит от конкретного применения. Лично меня устроил вариант с приведением всех Decimal к float.

original post http://vasnake.blogspot.com/2013/04/python-decimal-jsondumpsloads.html

Комментариев нет:

Отправить комментарий

Архив блога

Ярлыки

linux (241) python (191) citation (186) web-develop (170) gov.ru (159) video (124) бытовуха (115) sysadm (100) GIS (97) Zope(Plone) (88) бурчалки (84) Book (83) programming (82) грабли (77) Fun (76) development (73) windsurfing (72) Microsoft (64) hiload (62) internet provider (57) opensource (57) security (57) опыт (55) movie (52) Wisdom (51) ML (47) driving (45) hardware (45) language (45) money (42) JS (41) curse (40) bigdata (39) DBMS (38) ArcGIS (34) history (31) PDA (30) howto (30) holyday (29) Google (27) Oracle (27) tourism (27) virtbox (27) health (26) vacation (24) AI (23) Autodesk (23) SQL (23) humor (23) Java (22) knowledge (22) translate (20) CSS (19) cheatsheet (19) hack (19) Apache (16) Klaipeda (15) Manager (15) web-browser (15) Никонов (15) functional programming (14) happiness (14) music (14) todo (14) PHP (13) course (13) scala (13) weapon (13) HTTP. Apache (12) SSH (12) frameworks (12) hero (12) im (12) settings (12) HTML (11) SciTE (11) USA (11) crypto (11) game (11) map (11) HTTPD (9) ODF (9) Photo (9) купи/продай (9) benchmark (8) documentation (8) 3D (7) CS (7) DNS (7) NoSQL (7) cloud (7) django (7) gun (7) matroska (7) telephony (7) Microsoft Office (6) VCS (6) bluetooth (6) pidgin (6) proxy (6) Donald Knuth (5) ETL (5) NVIDIA (5) Palanga (5) REST (5) bash (5) flash (5) keyboard (5) price (5) samba (5) CGI (4) LISP (4) RoR (4) cache (4) car (4) display (4) holywar (4) nginx (4) pistol (4) spark (4) xml (4) Лебедев (4) IDE (3) IE8 (3) J2EE (3) NTFS (3) RDP (3) holiday (3) mount (3) Гоблин (3) кухня (3) урюк (3) AMQP (2) ERP (2) IE7 (2) NAS (2) Naudoc (2) PDF (2) address (2) air (2) british (2) coffee (2) fitness (2) font (2) ftp (2) fuckup (2) messaging (2) notify (2) sharepoint (2) ssl/tls (2) stardict (2) tests (2) tunnel (2) udev (2) APT (1) Baltic (1) CRUD (1) Canyonlands (1) Cyprus (1) DVDShrink (1) Jabber (1) K9Copy (1) Matlab (1) Portugal (1) VBA (1) WD My Book (1) autoit (1) bike (1) cannabis (1) chat (1) concurrent (1) dbf (1) ext4 (1) idioten (1) join (1) krusader (1) license (1) life (1) migration (1) mindmap (1) navitel (1) pneumatic weapon (1) quiz (1) regexp (1) robot (1) science (1) seaside (1) serialization (1) shore (1) spatial (1) tie (1) vim (1) Науру (1) крысы (1) налоги (1) пианино (1)