python Json的一点收获,自定义序列化方法

来源:互联网 发布:工程成本核算软件 编辑:程序博客网 时间:2024/05/19 03:21

From: http://blog.csdn.net/jlnuboy/article/details/5732196

PyMOTW: json

  • 模块: json
  • 目的: JavaScript对象格式序列器
  • python版本: 2.6

json模块提供了一个类似于pickle中用于转换内存中python对象为一个序列表示形式(“JavaScript Object Notation”)的API接口. 但和pickle不同的是, JSON在其他很多语言中都有对应实现(特别是在JavaScript中), 使其更适用于内部应用间的通信. 在一个AJAX应用中, JSON可能对于web服务器和客户端间的通信, 使用最为广泛, 但它也不仅限于这类应用.

 

 

 

简单数据类型的编码和解码

编码器默认支持Python的本地类型(如int, float, list, tuple, dict).

 

 

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  3. print 'DATA:', repr(data)  
  4. data_string = json.dumps(data)  
  5. print 'JSON:', data_string  
 

编码器处理之后的值和Python的repr()的输出值很类似.

[python] view plaincopy
  1. $ python json_simple_types.py  
  2. DATA: [{'a''A''c'3.0'b': (24)}]  
  3. JSON: [{"a""A""c"3.0"b": [24]}]  
 

编码之后的解码所获的的值可能和原先的对象不是完全一致.

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  3. data_string = json.dumps(data)  
  4. print 'ENCODED:', data_string  
  5. decoded = json.loads(data_string)  
  6. print 'DECODED:', decoded  
  7. print 'ORIGINAL:', type(data[0]['b'])  
  8. print 'DECODED :', type(decoded[0]['b'])  
 

比如说, 元组会被转换为JSON的列表.

[python] view plaincopy
  1. $ python json_simple_types_decode.py  
  2. ENCODED: [{"a""A""c"3.0"b": [24]}]  
  3. DECODED: [{u'a': u'A', u'c'3.0, u'b': [24]}]  
  4. ORIGINAL: <type 'tuple'>  
  5. DECODED : <type 'list'>  
 

 

人性化使用 vs 紧凑型输出

JSON优于pickle的另外一点是其结果具有可读性. dumps()函数接收多个参数用于更好的输出结构. 比如说. sort_keys参数告诉编码器按照顺序输出字典的键值, 而不是随机无序的.

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  3. print 'DATA:', repr(data)  
  4. unsorted = json.dumps(data)  
  5. print 'JSON:', json.dumps(data)  
  6. print 'SORT:', json.dumps(data, sort_keys=True)  
  7. first = json.dumps(data, sort_keys=True)  
  8. second = json.dumps(data, sort_keys=True)  
  9. print 'UNSORTED MATCH:', unsorted == first  
  10. print 'SORTED MATCH :', first == second  
 

排序之后更容易让人看出结果, 也使进行JSON的比较输出成为可能.

 

[python] view plaincopy
  1. $ python json_sort_keys.py  
  2. DATA: [{'a''A''c'3.0'b': (24)}]  
  3. JSON: [{"a""A""c"3.0"b": [24]}]  
  4. SORT: [{"a""A""b": [24], "c"3.0}]  
  5. UNSORTED MATCH: False  
  6. SORTED MATCH : True  
 

对于高度嵌套的数据结构, 你会想在输出结果中增加缩进以更好的显示其格式.

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  3. print 'DATA:', repr(data)  
  4. print 'NORMAL:', json.dumps(data, sort_keys=True)  
  5. print 'INDENT:', json.dumps(data, sort_keys=True, indent=2)  
 

当indent参数是一非负整数时, 输出的结构和pprint更为接近, 在每个缩进层次上都有前导空格.

[python] view plaincopy
  1. $ python json_indent.py  
  2. DATA: [{'a''A''c'3.0'b': (24)}]  
  3. NORMAL: [{"a""A""b": [24], "c"3.0}]  
  4. INDENT: [  
  5.              {  
  6.              "a""A",  
  7.              "b": [  
  8.                       2,  
  9.                       4  
  10.                       ],  
  11.              "c"3.0  
  12.              }  
  13. ]  
 

像这种类型输出的数据在传输过程中需占用更多的字节, 不过, 在实际生产环境中没有必要使用缩进格式. 实际上, 你可以设置数据的分隔符来让结果更为紧凑.

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  3. print 'DATA:', repr(data)  
  4. print 'repr(data) :', len(repr(data))  
  5. print 'dumps(data) :', len(json.dumps(data))  
  6. print 'dumps(data, indent=2) :', len(json.dumps(data, indent=2))  
  7. print 'dumps(data, separators):', len(json.dumps(data, separators=(',',':')))  
 

dumps()函数的separators参数是一个元组, 包含分隔列表各项和字典键值各项的字符串. 默认是(‘, ‘, ‘: ‘). 可以去掉后者中的空格, 我们可以得到较紧凑的输出.

[python] view plaincopy
  1. $ python json_compact_encoding.py  
  2. DATA: [{'a''A''c'3.0'b': (24)}]  
  3. repr(data) : 35  
  4. dumps(data) : 35  
  5. dumps(data, indent=2) : 76  
  6. dumps(data, separators): 29  
 

 

编码字典

JSON格式中, 字典的键被限制为字符串类型. 如果字典中的键是其他类型, 那么在编码这个对象时会产生一个TypeError异常. 一种解决这个限制的方法是, 在编码时, 使用skipkeys参数跳过所有非字符串类型的键.

[python] view plaincopy
  1. import json  
  2. data = [ { 'a':'A''b':(24), 'c':3.0, ('d',):'D tuple' } ]  
  3. print 'First attempt'  
  4. try:  
  5.     print json.dumps(data)  
  6. except TypeError, err:  
  7.     print 'ERROR:', err  
  8. print  
  9. print 'Second attempt'  
  10. print json.dumps(data, skipkeys=True)  
 

非字符串类型的键被忽略, 而不抛出一个异常.

[python] view plaincopy
  1. $ python json_skipkeys.py  
  2. First attempt  
  3. ERROR: key ('d',) is not a string  
  4. Second attempt  
  5. [{"a""A""c"3.0"b": [24]}]  
 

 

自定义类型的处理

上面所有的例子都是用了Python的内置类型作为例子, 因为他们都被json本身支持. 当然, 自定义类型也常常需要正确编码. 这里有两种情况:

第一, 对于一个类的编码:

[python] view plaincopy
  1. class MyObj(object):  
  2.     def __init__(self, s):  
  3.         self.s = s  
  4.     def __repr__(self):  
  5.         return '<MyObj(%s)>' % self.s  
 

编码一个MyObj对象的最简单方式是定义个转换函数, 用于将位置类型转换出呢个已知类型. 你没有必要自己进行编码, 而仅需要将一个对象转换成另一个对象.

[python] view plaincopy
  1. import json  
  2. import json_myobj  
  3. obj = json_myobj.MyObj('instance value goes here')  
  4. print 'First attempt'  
  5. try:  
  6.     print json.dumps(obj)  
  7. except TypeError, err:  
  8.     print 'ERROR:', err  
  9. def convert_to_builtin_type(obj):  
  10.     print 'default(', repr(obj), ')'  
  11.     # Convert objects to a dictionary of their representation  
  12.     d = { '__class__':obj.__class__.__name__,  
  13.           '__module__':obj.__module__,  
  14.         }  
  15.     d.update(obj.__dict__)  
  16.     return d  
  17. print  
  18. print 'With default'  
  19. print json.dumps(obj, default=convert_to_builtin_type)  
 

在convert_to_builtin_type()函数中, 不被json识别的类对象被转换成一个包含足够能重建这个对象的字典信息.

[python] view plaincopy
  1. $ python json_dump_default.py  
  2. First attempt  
  3. ERROR: <MyObj(instance value goes here)> is not JSON serializable  
  4. With default  
  5. default( <MyObj(instance value goes here)> )  
  6. {"s""instance value goes here""__module__""json_myobj""__class__""MyObj"}  
 

 

为了能解码结果数据并创建一个MyObj实例, 我们需要配合解码器以便可以从模块中导入类并创建实例. 我们在loads()函数中使用object_hook参数.

在输入数据流中, 对于解码获得的每个字典都会调用object_hook, 将这个字典转换成其他类型的对象. hook函数返回的是调用程序所需要的对象, 而不是字典.

[python] view plaincopy
  1. import json  
  2. def dict_to_object(d):  
  3.     if '__class__' in d:  
  4.         class_name = d.pop('__class__')  
  5.         module_name = d.pop('__module__')  
  6.         module = __import__(module_name)  
  7.         print 'MODULE:', module  
  8.         class_ = getattr(module, class_name)  
  9.         print 'CLASS:'class_  
  10.         args = dict( (key.encode('ascii'), value) for key, value in d.items())  
  11.         print 'INSTANCE ARGS:', args  
  12.         inst = class_(**args)  
  13.     else:  
  14.         inst = d  
  15.     return inst  
  16. encoded_object = '[{"s": "instance value goes here", "__module__": "json_myobj", "__class__": "MyObj"}]'  
  17. myobj_instance = json.loads(encoded_object, object_hook=dict_to_object)  
  18. print myobj_instance  
 

由于json将字符串值转换成unicode对象, 所以我们需要将作为类构造器的参数重新编码为ASCII字符串.

[python] view plaincopy
  1. $ python json_load_object_hook.py  
  2. MODULE: <module 'json_myobj' from '/Users/dhellmann/Documents/PyMOTW/src/PyMOTW/json/json_myobj.pyc'>  
  3. CLASS: <class 'json_myobj.MyObj'>  
  4. INSTANCE ARGS: {'s': u'instance value goes here'}  
  5. [<MyObj(instance value goes here)>]  
 

 

对于内置类型也都有类似的hooks, 如整型(parse_int), 浮点型(parse_float), 常量(parse_constant).

编码和解码类

除了上述的这些函数外, json模块还提供了编码和解码类. 直接使用这些类, 你可以访问到额外的API接口或者定制创建它的子类.

JSONEncoder提供了一个产生编码数据”块”的的迭代接口, 这在写入一个文件或网络sockets时(不需要在内存中完整表示整个数据)是非常方便的,

[python] view plaincopy
  1. import json  
  2. encoder = json.JSONEncoder()  
  3. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  4. for part in encoder.iterencode(data):  
  5.     print 'PART:', part  
 

正如你看到的, 数据是以逻辑单位形式输出的, 而不是按照数据长度输出.

[python] view plaincopy
  1. $ python json_encoder_iterable.py  
  2.      PART: [  
  3.              PART: {  
  4.              PART: "a"  
  5.              PART: :  
  6.              PART: "A"  
  7.              PART: ,  
  8.              PART: "c"  
  9.              PART: :  
  10.              PART: 3.0  
  11.              PART: ,  
  12.              PART: "b"  
  13.              PART: :  
  14.              PART: [  
  15.              PART: 2  
  16.              PART: ,  
  17.              PART: 4  
  18.              PART: ]  
  19.              PART: }  
  20.              PART: ]  
 

 

encode()方法基本上等价于’‘.join(encoder.iterencode()), 只是多了些附加错误检查.

为了能够编码任何类型的对象, 我们可以编写一类似于上述的convert_to_builtin_type()函数去重载default()方法.

[python] view plaincopy
  1. import json  
  2. import json_myobj  
  3. class MyEncoder(json.JSONEncoder):  
  4.     def default(self, obj):  
  5.         print 'default(', repr(obj), ')'  
  6.         # Convert objects to a dictionary of their representation  
  7.         d = { '__class__':obj.__class__.__name__,  
  8.               '__module__':obj.__module__,  
  9.             }  
  10.         d.update(obj.__dict__)  
  11.         return d  
  12. obj = json_myobj.MyObj('internal data')  
  13. print obj  
  14. print MyEncoder().encode(obj)  
 

这里输出的结果是和先前的实现一致的.

[python] view plaincopy
  1. $ python json_encoder_default.py  
  2. <MyObj(internal data)>  
  3. default( <MyObj(internal data)> )  
  4. {"s""internal data""__module__""json_myobj""__class__""MyObj"}  
 

解码后将字典转换成一个对象, 在先前实现的基础上稍作修改即可.

[python] view plaincopy
  1. import json  
  2. class MyDecoder(json.JSONDecoder):  
  3.     def __init__(self):  
  4.         json.JSONDecoder.__init__(self, object_hook=self.dict_to_object)  
  5.     def dict_to_object(self, d):  
  6.         if '__class__' in d:  
  7.             class_name = d.pop('__class__')  
  8.             module_name = d.pop('__module__')  
  9.             module = __import__(module_name)  
  10.             print 'MODULE:', module  
  11.             class_ = getattr(module, class_name)  
  12.             print 'CLASS:'class_  
  13.             args = dict( (key.encode('ascii'), value) for key, value in d.items())  
  14.             print 'INSTANCE ARGS:', args  
  15.             inst = class_(**args)  
  16.         else:  
  17.             inst = d  
  18.         return inst  
  19. encoded_object = '[{"s": "instance value goes here", "__module__": "json_myobj", "__class__": "MyObj"}]'  
  20. myobj_instance = MyDecoder().decode(encoded_object)  
  21. print myobj_instance  
 

输出结果也是和先前例子中输出的一样.

[python] view plaincopy
  1. $ python json_decoder_object_hook.py  
  2. MODULE: <module 'json_myobj' from '/Users/dhellmann/Documents/PyMOTW/src/PyMOTW/json/json_myobj.pyc'>  
  3. CLASS: <class 'json_myobj.MyObj'>  
  4. INSTANCE ARGS: {'s': u'instance value goes here'}  
  5. [<MyObj(instance value goes here)>]  
 

 

流和文件的处理

到目前为止的所有例子, 我们都假设待编码的数据都是一次性完整加载到内存中的. 但对于大型数据结构来说, 将编码数据直接写入一个类文件对象, 可能会更好. load()和dump()函数可以接收一个用于读或写的类文件对象的引用.

[python] view plaincopy
  1. import json  
  2. import tempfile  
  3. data = [ { 'a':'A''b':(24), 'c':3.0 } ]  
  4. f = tempfile.NamedTemporaryFile(mode='w+')  
  5. json.dump(data, f)  
  6. f.flush()  
  7. print open(f.name, 'r').read()  
 

对于socket来说, 也和正常文件句柄类似.

[python] view plaincopy
  1. $ python json_dump_file.py  
  2. [{"a""A""c"3.0"b": [24]}]  
 

虽然一次性读取部分数据不是很好, 但是load()函数仍然提供了从流数据输入中封装生成对象的功能.

[python] view plaincopy
  1. import json  
  2. import tempfile  
  3. f = tempfile.NamedTemporaryFile(mode='w+')  
  4. f.write('[{"a": "A", "c": 3.0, "b": [2, 4]}]')  
  5. f.flush()  
  6. f.seek(0)  
  7. print json.load(f)  
 
[python] view plaincopy
  1. $ python json_load_file.py  
  2. [{u'a': u'A', u'c'3.0, u'b': [24]}]  
 

 

混合数据流

JSONDecoder包含了raw_decode()方法, 用于解码在很多数据组成的数据结构, 例如包含多余文本的JSON数据. 返回的值是从输入数据中解码获得的对象, 数据中的index表示解码对象结束时所在的位置.

[python] view plaincopy
  1. import json  
  2. decoder = json.JSONDecoder()  
  3. def get_decoded_and_remainder(input_data):  
  4.     obj, end = decoder.raw_decode(input_data)  
  5.     remaining = input_data[end:]  
  6.     return (obj, end, remaining)  
  7. encoded_object = '[{"a": "A", "c": 3.0, "b": [2, 4]}]'  
  8. extra_text = 'This text is not JSON.'  
  9. print 'JSON first:'  
  10. obj, end, remaining = get_decoded_and_remainder(' '.join([encoded_object, extra_text]))  
  11. print 'Object :', obj  
  12. print 'End of parsed input :', end  
  13. print 'Remaining text :', repr(remaining)  
  14. print  
  15. print 'JSON embedded:'  
  16. try:  
  17.     obj, end, remaining = get_decoded_and_remainder(  
  18.                  ' '.join([extra_text, encoded_object, extra_text])  
  19.                  )  
  20. except ValueError, err:  
  21.     print 'ERROR:', err  
 

不幸的是, 这仅仅在对象出现在输入流的开始处才有效.

[python] view plaincopy
  1. $ python json_mixed_data.py  
  2. JSON first:  
  3. Object : [{u'a': u'A', u'c'3.0, u'b': [24]}]  
  4. End of parsed input : 35  
  5. Remaining text : ' This text is not JSON.'  
  6. JSON embedded:  
  7. ERROR: No JSON object could be decoded