如何检查Python中神秘的反序列化对象
我正在尝试把JSON数据加载回一个对象里。虽然“loads”这个方法似乎没有报错,但我发现对象里的属性和我预期的不太一样。
我该如何检查我手里的这个对象呢?(这是基于网页的代码)
results = {"Subscriber": {"firstname": "Neal", "lastname": "Walters"}}
subscriber = json.loads(results)
for item in inspect.getmembers(subscriber):
self.response.out.write("<BR>Item")
for subitem in item:
self.response.out.write("<BR> SubItem=" + subitem)
上面的尝试返回了这个结果:
Item
SubItem=__class__
我觉得这可能不重要,但为了提供一些背景信息: 这个JSON实际上是通过Google App Engine的urlfetch从一个REST网络服务获取的, 这个服务是用这个工具创建的: http://code.google.com/p/appengine-rest-server。 数据是从一个数据存储中获取的,定义如下:
class Subscriber(db.Model):
firstname = db.StringProperty()
lastname = db.StringProperty()
谢谢, Neal
更新 #1:基本上,我是在尝试把JSON反序列化回一个对象。 理论上,它是从一个对象序列化过来的,现在我想把它再变回对象。 也许更好的问题是,怎么做到这一点呢?
更新 #2:我试图把一个复杂的程序简化成几行代码,所以在“伪代码”时犯了一些错误,以便在这里发布。
这是一个更好的代码示例,现在可以在我电脑上运行。
results = '{"Subscriber": {"firstname": "Neal", "lastname": "Walters"}}'
subscriber = json.loads(results)
for key, value in subscriber.items():
print " %s: %s" %(key, value)
上面的代码可以运行,但显示的内容看起来和JSON字符串本身没有什么区别。它显示的是: 订阅者:{u'lastname': u'Walters', u'firstname': u'Neal'}
我更熟悉微软的背景,所以当我听到序列化/反序列化时,我想到的是从对象变成字符串,再从字符串变回对象。所以如果我把数据序列化成JSON,然后再反序列化,我得到的是什么呢?是字典、列表,还是对象?实际上,我是通过一个REST网络方法获取JSON的,这个方法是帮我把对象序列化的。
理想情况下,我想要一个和我上面定义的Subscriber类匹配的订阅者对象,而且我不想为每个类都写特定的代码,因为我还想对其他很多类做同样的事情。如果我必须写一些自定义代码,我需要写得更通用,以便适用于任何类。
更新 #3:这是为了进一步解释我认为这是一个必要工具的原因。我正在写一个大型应用,可能会在Google App Engine(GAE)上运行。我们倾向于使用REST架构,原因有很多,其中之一是我们的网页界面应该通过REST网络层访问数据存储。(我更习惯SOAP,所以切换到REST本身就是一个小挑战)。所以获取和更新数据的经典方法之一是通过业务或数据层。通过使用上面提到的REST工具,我可以选择XML或JSON。我希望在开发大型应用之前,先做一个小的工作原型。
然后,假设我们有一个成功的应用,而GAE的价格翻倍了。那么我们可以只重写数据层,把我们的Python/Django用户层(网页代码)迁移到亚马逊或其他地方。
如果我要做这些,为什么我还想要所有东西都是字典对象呢?难道我不想要完整的类结构的强大功能吗?接下来一个技巧是进行一种对象关系映射(ORM),这样我们就不必暴露确切的数据表,而是提供一个更逻辑的层。
我们还想向付费用户提供一个RESTful API,他们可能使用任何语言。对于他们来说,他们可以使用XML或JSON,而不会使用这里讨论的序列化例程。
3 个回答
我猜 loads
返回的是一个字典。要遍历它的内容,可以使用类似下面的代码:
for key, value in subscriber.items():
self.response.out.write("%s: %s" %(key, value))
你代码里的results
是一个字典,而不是字符串,所以用json.loads
会出错。如果这个问题解决了,内层循环中的每个subitem
都是一个元组,你现在试图把它加到一个字符串上,这样也会出错。我猜你是为了简化代码,但这两个类型错误说明你简化得太过了(而且是错误的)。为什么不使用一个(同样简化的)有效代码片段,以及你想用json.loads
处理的实际字符串,而不是一个根本无法复现你问题的代码呢?这样会更容易帮助你。
除了查看实际的字符串和显示一些明显的信息,比如type(subscriber)
,很难在这段明显有问题的代码和不足的信息基础上提供更多帮助:-(。
编辑:在“update2”中,提问者说
It displays this: Subscriber: {u'lastname': u'Walters', u'firstname': u'Neal'}
...那还有什么可能显示呢,拜托?!你把键打印成字符串,然后把值也打印成字符串——键就是字符串,而值是另一个字典,所以当然会“字符串化”(而且JSON中的所有字符串都是Unicode——就像在C#或Java中一样,你说你来自微软背景,那为什么这让你感到惊讶呢?!)。str(somedict)
和repr(somedict)
显示的是键和值的repr
(周围有大括号,冒号和逗号作为适当的分隔符)。
JSON
是一种完全独立于语言的序列化格式,虽然最初是围绕Javascript设计的,但它根本不知道你期望看到什么类的实例(当然它不知道,这样想是荒谬的:如果它硬编码了“类”的概念,那它怎么可能是独立于语言的呢?很多语言,包括Javascript,根本就没有这个概念!)——所以它使用(在Python中)字符串、数字、列表和字典(这四种非常基本的数据类型,任何一个合格的现代语言都应该至少在某个库中有这些类型,如果不是语言本身内置的话!)。当你用json.loads
处理一个字符串时,你总是会得到上述四种数据类型的某种嵌套组合(顺便说一下,所有字符串都是Unicode,所有数字都是浮点数;-)。
如果你不知道(并且不想通过某种任意约定来编码)哪些类的实例正在被序列化,但在反序列化时绝对必须得到类的实例(而不仅仅是字典等),那么JSON本身无法帮助你——这种元信息不可能存在于JSON序列化的字符串中。
如果你对这四种基本类型没问题,只是想看到一些你认为比Python默认的字符串打印结果“更好看”的打印结果,你就需要根据你自己对“好看”的主观定义,编写一个递归的美化打印函数(我怀疑你会比现在的结果更喜欢Python自己的pprint
标准库模块;-)。
JSON只支持编码字符串、浮点数、整数、JavaScript对象(类似Python中的字典)和列表。
你需要写一个函数,把返回的字典转换成一个类,然后通过json.loads
函数,把这个类和JSON字符串一起传进去,使用object_hook
这个参数。下面是一些具体的代码示例:
import json
class Subscriber(object):
firstname = None
lastname = None
class Post(object):
author = None
title = None
def decode_from_dict(cls,vals):
obj = cls()
for key, val in vals.items():
setattr(obj, key, val)
return obj
SERIALIZABLE_CLASSES = {'Subscriber': Subscriber,
'Post': Post}
def decode_object(d):
for field in d:
if field in SERIALIZABLE_CLASSES:
cls = SERIALIZABLE_CLASSES[field]
return decode_from_dict(cls, d[field])
return d
results = '''[{"Subscriber": {"firstname": "Neal", "lastname": "Walters"}},
{"Post": {"author": {"Subscriber": {"firstname": "Neal",
"lastname": "Walters"}}},
"title": "Decoding JSON Objects"}]'''
result = json.loads(results, object_hook=decode_object)
print result
print result[1].author
这个方法适用于那些可以不带参数创建的类,并且可以使用setattr
来设置属性。
另外,这里用的是json
库。我对simplejson
没有经验,所以可能会有不同,但我听说它们是一样的。
需要注意的是,虽然两个订阅者对象的值是一样的,但生成的对象却不相同。这个问题可以通过对decode_from_dict
类进行缓存来解决。