JSON кодировать / декодировать перечисления GTK
Я должен сохранить различные свойства пользовательских элементов GTK в файл для будущего использования и решил использовать JSON из-за простого формата и вложенности слов.
Многие свойства являются перечислениями GTK, как gtk.PAGE_ORIENTATION_PORTRAIT
, gtk.ANCHOR_CENTER
а также pango.ALIGN_LEFT
, У них есть уникальное имя, которое можно получить с помощью obj.value_name
чтобы получить действительный тип JSON.
В настоящее время у меня есть 2 метода для каждого из моих элементов: to_str()
чтобы получить имя_значения и from_str()
который отображает str в enum снова. Я хотел бы автоматизировать это, чтобы не забыть их вызвать и немного очистить код. JSONEncoder и JSONDecodr делают именно это, или я подумал...
Это пример, приведенный в документации по Python, и он работает как положено.
import json
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
print "default method called for:", obj
if isinstance(obj, complex):
return [obj.real, obj.imag]
return json.JSONEncoder.default(self, obj)
print json.dumps(2 + 1j, cls=ComplexEncoder)
Основываясь на этом примере, я добавил перечисления GTK:
import json
import gtk
ENUMS = [gtk.PAGE_ORIENTATION_PORTRAIT, gtk.PAGE_ORIENTATION_LANDSCAPE]
class GtkEncoder(json.JSONEncoder):
def default(self, obj):
print "default method called for:", obj
if obj in ENUMS:
return obj.value_name
return json.JSONEncoder.default(self, obj)
print json.dumps(gtk.PAGE_ORIENTATION_LANDSCAPE, cls=GtkEncoder)
Обратите внимание на добавленный оператор печати в default
метод. В исходном примере этот метод вызывается без проблем, но не в примере GTK. default
метод никогда не вызывается и возвращает <enum GTK_PAGE_ORIENTATION_LANDSCAPE of type GtkPageOrientation>
который не является действительным JSON конечно.
Итак, есть ли способ автоматически кодировать / декодировать эти перечисления, или я застрял с текущим ручным подходом? Обратите внимание, что моя структура данных для дампа - это не одно значение, а дикт или дикт.
2 ответа
Наблюдаемое поведение происходит потому, что значение gtk.PAGE_ORIENTATION_LANDSCAPE
это пример class 'gtk._gtk.PageOrientation'
который наследует type 'gobject.GEnum'
который в свою очередь наследует type 'int'
,
Таким образом, ваши GTK-перечисления являются целыми числами, а код json предполагает, что он может обрабатывать целые числа и, следовательно, не вызывает default
метод вашего кодировщика.
К сожалению, текущая реализация json не так полезна для кодирования подклассных типов, как это: / / Нет наследования и переопределения делает это возможным (по крайней мере, я не могу найти никакого решения для этого). Просто слишком много жестко закодированных мест, где проверяется значение isinstance(value, (int, long))
,
Но вы, конечно, можете исправить источник кодера json, чтобы достичь своей цели без необходимости заново реализовывать всю функциональность json. Для этого скопируйте файл encoder.py
из библиотеки JSON (для меня это /usr/lib/python2.7/json/encoder.py
) в ваш рабочий каталог и залатайте его.
В функции _iterencode_list()
а также _iterencode_dict()
(они локальны для функционирования _make_iterencode()
) вы можете найти чеки на предмет типа int
или же long
; если это так, текущая реализация просто вызывает str(value)
, Изменить это на encodeInt(value)
(в трех местах!) и реализовать свой собственный encodeInt()
функция в encoder.py
:
def encodeInt(value):
try:
return value.value_name
except:
return str(value)
Затем в исходном коде вам придется импортировать непосредственно этот исправленный файл:
import encoder
и вам нужно будет убедиться, что реализация C больше не используется, а вместо этого исправлен код. (Как видите, обычно используется (более быстрая) реализация C, а код Python, в котором мы что-то исправили, не является.) Для этого просто добавьте после импорта:
encoder.c_make_encoder = None
Теперь ваш исправленный кодер можно использовать:
print encoder.JSONEncoder().encode({
gtk.PAGE_ORIENTATION_PORTRAIT: [
gtk.PAGE_ORIENTATION_LANDSCAPE
],
gtk.PAGE_ORIENTATION_LANDSCAPE: gtk.PAGE_ORIENTATION_PORTRAIT })
печатает:
{"GTK_PAGE_ORIENTATION_PORTRAIT": [GTK_PAGE_ORIENTATION_LANDSCAPE], "GTK_PAGE_ORIENTATION_LANDSCAPE": GTK_PAGE_ORIENTATION_PORTRAIT}
Обратите внимание, что ключи Json dict всегда должны быть строками. Вот почему ваши значения получают двойные кавычки при использовании в качестве ключей. Но это судьба, даже нормальная int
s - при использовании в качестве ключей - поделиться. Они также становятся строковыми.
Вы можете взглянуть на http://pastebin.com/2HAtN9E8 чтобы увидеть все источники.
Я нашел решение, хотя это может не сработать для gtk, однако оно работает для перечислений в целом.
Если вы можете использовать IntEnum вместо Enum, то вы можете переопределить str в своем классе Enum, чтобы вернуть строку того, что вы хотите, имя или значение, скорее всего.
import json
from enum import IntEnum
class TrekCaptains(IntEnum):
Kirk = 0
Picard = 1
def __str__(self):
return '{0}'.format(self.value)
s = {TrekCaptains.Kirk:'Original Series',
TrekCaptains.Picard:'Next Generation'}
j = json.dumps(s)
print j
#result
#{"0": "Original Series", "1": "Next Generation"}