JSON编码/解码GTK枚举

时间:2014-02-06 14:47:17

标签: python json gtk pygtk

我必须将自定义GTK元素的各种属性保存到文件中以供将来使用,并决定使用JSON,因为它具有简单的格式和嵌套。

许多属性都是GTK枚举,例如gtk.PAGE_ORIENTATION_PORTRAITgtk.ANCHOR_CENTERpango.ALIGN_LEFT。它们具有唯一的名称,可以使用obj.value_name检索以获得有效的JSON类型。

目前我的每个元素都有2个方法:to_str()获取value_name,from_str()将str再次映射到枚举。我想自动化这个,所以我不会忘记调用这些并清理代码。 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方法中添加的print语句。在最初的示例中,调用此方法没有问题,但在GTK示例中没有。永远不会调用default方法,它会返回<enum GTK_PAGE_ORIENTATION_LANDSCAPE of type GtkPageOrientation>,这是无效的JSON ofcourse。

那么,有没有办法自动编码/解码这些枚举,还是我坚持使用当前的手动方法?请注意,我要转储的数据结构不是单个值,而是dict或dicts。

2 个答案:

答案 0 :(得分:2)

观察到的行为是因为值gtk.PAGE_ORIENTATION_LANDSCAPEclass '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()的本地函数)中,您可以找到intlong类型的检查;如果是这样,当前实现只调用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 - 当用作钥匙 - 分享时。他们也得到了字符串化。

您可以查看http://pastebin.com/2HAtN9E8以查看所有来源。

答案 1 :(得分:0)

我找到了一个解决方案,虽然这可能不适用于gtk,但它确实适用于枚举。

如果您可以使用IntEnum而不是Enum,那么您可以覆盖Enum类中的 str ,以返回您想要的任何名称或名称或值的字符串。

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"}