2012-04-20 92 views
73

我正尝试创建类实例的JSON字符串表示形式并且遇到困难。比方说,类是建立这样的:将类实例序列化为JSON

class testclass: 
    value1 = "a" 
    value2 = "b" 

调用到json.dumps作出这样的:

t = testclass() 
json.dumps(t) 

它失败并告诉我,识别TestClass不是JSON序列化。

TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable 

我也用泡菜模块尝试:

t = testclass() 
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL)) 

,它给类实例的信息,但类实例的不是一个序列化的内容。

b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.' 

我在做什么错?

+0

http://stackoverflow.com/questions/2343535/easiest-way-to-serialize-a-simple-class-object-with-simplejson – delicateLatticeworkFever 2012-04-20 19:10:28

+3

使用一行,'S = json.dumps(OBJ ,default = lambda x:x .__ dict __)',序列化对象的实例变量('self.value1','self.value2',...)。它是最简单也是最直接的方式。它会序列化嵌套的对象结构。当任何给定对象不是可直接序列化时,调用'default'函数。你也可以看看我的答案。我发现流行的答案不必要的复杂,这可能是相当长的一段时间。 – codeman48 2017-12-13 07:25:02

+0

您的'testclass'没有'__init __()'方法,因此所有实例将共享类语句中定义的相同的两个类属性('value1'和'value2')。你是否理解一个类和一个实例之间的区别? – martineau 2018-01-30 02:44:33

回答

100

基本问题是JSON编码器json.dumps()只知道如何序列化一组有限的对象类型,默认情况下是所有内置类型。这里列出:https://docs.python.org/3.3/library/json.html#encoders-and-decoders

一个很好的解决办法是从JSONEncoder让你的类继承,然后实现JSONEncoder.default()功能,使该函数发出正确的JSON类。

一个简单的解决方案是在该实例的.__dict__成员上调用json.dumps()。这是一个标准的Python dict,如果你的类很简单,它将是JSON序列化的。

class Foo(object): 
    def __init__(self): 
     self.x = 1 
     self.y = 2 

foo = Foo() 
s = json.dumps(foo) # raises TypeError with "is not JSON serializable" 

s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2} 

上述方法在本博客中讨论:

        Serializing arbitrary Python objects to JSON using __dict__

注:我已编辑这个答案;原始版本仅讨论了.__dict__序列化方法。

+1

我试过了。调用json.dumps(t .__ dict__)的最终结果就是{}。 – ferhan 2012-04-20 19:11:51

+5

这是因为你的类没有'.__ init __()'方法函数,所以你的类实例有一个空字典。换句话说,'{}'是你的示例代码的正确结果。 – steveha 2012-04-20 19:37:37

+2

谢谢。这是诀窍。我添加了一个简单的没有参数的__init__,现在调用json.dumps(t .__ dict__)返回格式为: {“value2”:“345”,“value1”:“123”} 我有以前看过的帖子,不确定是否需要为成员定制序列化程序,需要__init__没有明确提及或者我错过了。谢谢。 – ferhan 2012-04-20 19:56:39

2

JSON并非真正用于序列化任意Python对象。对于序列化dict对象来说很好,但pickle模块实际上是您应该使用的一般模块。来自pickle的输出不是真正的人类可读的,但它应该没有问题。如果你坚持使用JSON,你可以看看​​模块,这是一个有趣的混合方法。

https://github.com/jsonpickle/jsonpickle

+6

我在pickle中看到的主要问题是它是Python特定的格式,而JSON是平台无关的格式。如果您为某些移动应用程序编写Web应用程序或后端,JSON特别有用。 已经有人说,谢谢你指出jsonpickle。 – 2014-05-27 11:50:07

+0

@Haroldo_OK不jsonpickle仍然导出到JSON,只是不太可读的人类? – Caelum 2015-11-24 10:51:15

15

我只是做:

data=json.dumps(myobject.__dict__) 

这是不完整的答案,如果你有某种复杂的对象类的你肯定不会得到一切。但是,我用这个为我的一些简单的对象。

它能够很好地工作的是从OptionParser模块获得的“选项”类。 它与JSON请求本身一起。

def executeJson(self, url, options): 
     data=json.dumps(options.__dict__) 
     if options.verbose: 
      print data 
     headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 
     return requests.post(url, data, headers=headers) 
+0

如果你不在课堂上使用,你可能想要删除自己。 – SpiRail 2013-07-10 14:37:45

+1

只要对象不是由其他对象组成,那就可以工作了。 – 2014-05-27 11:56:10

11

还有为我的作品伟大的,你可以尝试的一种方法:

json.dumps()可以采取一个可选参数默认在这里你可以对未知类型指定一个自定义序列化功能,这在我的情况下,貌似

def serialize(obj): 
    """JSON serializer for objects not serializable by default json code""" 

    if isinstance(obj, date): 
     serial = obj.isoformat() 
     return serial 

    if isinstance(obj, time): 
     serial = obj.isoformat() 
     return serial 

    return obj.__dict__ 

前两个IFS的日期和时间序列 ,然后有一个obj.__dict__返回任何其他对象。

最终调用如下:

json.dumps(myObj, default=serialize) 

这是特别好,当你序列化的集合,你不想显式调用__dict__为每个对象。这里是自动完成的。

到目前为止我对我的工作非常好,期待着您的想法。

+0

非常感谢这个答案 - 它帮助了我很多。 – Caribou 2017-03-06 10:09:38

+1

你应该得到更多的答案:)这是一个非常漂亮和优雅的解决方案! – keisar 2017-09-06 21:20:16

0

我相信,如接受的答案中所建议的,不是继承,而是使用多态。否则,你必须有一个很大的if语句来自定义每个对象的编码。这意味着创建一个JSON一个通用的默认编码器:

def jsonDefEncoder(obj): 
    if hasattr(obj, 'jsonEnc'): 
     return obj.jsonEnc() 
    else: #some default behavior 
     return obj.__dict__ 

,然后在要序列每个类jsonEnc()功能。例如

class A(object): 
    def __init__(self,lengthInFeet): 
     self.lengthInFeet=lengthInFeet 
    def jsonEnc(self): 
     return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter 

然后调用json.dumps(classInstance,default=jsonDefEncoder)

3

使用jsonpickle

import jsonpickle 

object = YourClass() 
json_object = jsonpickle.encode(object) 
3

您可以在json.dumps()功能指定default命名参数:

json.dumps(obj, default=lambda x: x.__dict__) 

说明:

表的文档(2.73.6):

``default(obj)`` is a function that should return a serializable version 
of obj or raise TypeError. The default simply raises TypeError. 

注(关于Python 2.7和Python 3.x的工程):在这种情况下,你需要instance变量与不变量class,作为问题中的例子试图去做。 (我假设提问者的意思是class instance是一个类的对象)

我从@ phihag的回答here第一次了解到这一点。发现它是做这项工作最简单,最干净的方式。

0

如何开始做这件事有一些很好的答案。但有一些事情要记住:

  • 如果实例嵌套在大型数据结构中,该怎么办?
  • 如果还想要类名呢?
  • 如果要反序列化实例,该怎么办?
  • 如果您使用__slots__而不是__dict__,该怎么办?
  • 如果你只是不想自己做?

json-tricks是一个图书馆(我做了和其他人的贡献),已经能够做到这一点了很长一段时间。例如:

class MyTestCls: 
    def __init__(self, **kwargs): 
     for k, v in kwargs.items(): 
      setattr(self, k, v) 

cls_instance = MyTestCls(s='ub', dct={'7': 7}) 

json = dumps(cls_instance, indent=4) 
instance = loads(json) 

你会得到你的实例。在这里,JSON看起来是这样的:

{ 
    "__instance_type__": [ 
     "json_tricks.test_class", 
     "MyTestCls" 
    ], 
    "attributes": { 
     "s": "ub", 
     "dct": { 
      "7": 7 
     } 
    } 
} 

如果你想使自己的解决方案,你可能看的json-tricks源,以免忘记某些特殊情况下(如__slots__)。

它也有其他类型,如numpy数组,日期时间,复数;它也允许评论。

1

这里有两个简单的函数用于序列化任何非复杂的类,没有什么花哨的,如前所述。

我将此用于配置类型的东西,因为我可以将新成员添加到没有代码调整的类。

import json 

class SimpleClass: 
    def __init__(self, a=None, b=None, c=None): 
     self.a = a 
     self.b = b 
     self.c = c 

def serialize_json(instance=None, path=None): 
    dt = {} 
    dt.update(vars(instance)) 

    with open(path, "w") as file: 
     json.dump(dt, file) 

def deserialize_json(cls=None, path=None): 
    def read_json(_path): 
     with open(_path, "r") as file: 
      return json.load(file) 

    data = read_json(path) 

    instance = object.__new__(cls) 

    for key, value in data.items(): 
     setattr(instance, key, value) 

    return instance 

# Usage: Create class and serialize under Windows file system. 
write_settings = SimpleClass(a=1, b=2, c=3) 
serialize_json(write_settings, r"c:\temp\test.json") 

# Read back and rehydrate. 
read_settings = deserialize_json(SimpleClass, r"c:\temp\test.json") 

# results are the same. 
print(vars(write_settings)) 
print(vars(read_settings)) 

# output: 
# {'c': 3, 'b': 2, 'a': 1} 
# {'c': 3, 'b': 2, 'a': 1}