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 всегда должны быть строками. Вот почему ваши значения получают двойные кавычки при использовании в качестве ключей. Но это судьба, даже нормальная ints - при использовании в качестве ключей - поделиться. Они также становятся строковыми.

Вы можете взглянуть на 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"}
Другие вопросы по тегам