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).
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- print 'DATA:', repr(data)
- data_string = json.dumps(data)
- print 'JSON:', data_string
编码器处理之后的值和Python的repr()的输出值很类似.
- $ python json_simple_types.py
- DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
- JSON: [{"a": "A", "c": 3.0, "b": [2, 4]}]
编码之后的解码所获的的值可能和原先的对象不是完全一致.
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- data_string = json.dumps(data)
- print 'ENCODED:', data_string
- decoded = json.loads(data_string)
- print 'DECODED:', decoded
- print 'ORIGINAL:', type(data[0]['b'])
- print 'DECODED :', type(decoded[0]['b'])
比如说, 元组会被转换为JSON的列表.
- $ python json_simple_types_decode.py
- ENCODED: [{"a": "A", "c": 3.0, "b": [2, 4]}]
- DECODED: [{u'a': u'A', u'c': 3.0, u'b': [2, 4]}]
- ORIGINAL: <type 'tuple'>
- DECODED : <type 'list'>
人性化使用 vs 紧凑型输出
JSON优于pickle的另外一点是其结果具有可读性. dumps()函数接收多个参数用于更好的输出结构. 比如说. sort_keys参数告诉编码器按照顺序输出字典的键值, 而不是随机无序的.
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- print 'DATA:', repr(data)
- unsorted = json.dumps(data)
- print 'JSON:', json.dumps(data)
- print 'SORT:', json.dumps(data, sort_keys=True)
- first = json.dumps(data, sort_keys=True)
- second = json.dumps(data, sort_keys=True)
- print 'UNSORTED MATCH:', unsorted == first
- print 'SORTED MATCH :', first == second
排序之后更容易让人看出结果, 也使进行JSON的比较输出成为可能.
- $ python json_sort_keys.py
- DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
- JSON: [{"a": "A", "c": 3.0, "b": [2, 4]}]
- SORT: [{"a": "A", "b": [2, 4], "c": 3.0}]
- UNSORTED MATCH: False
- SORTED MATCH : True
对于高度嵌套的数据结构, 你会想在输出结果中增加缩进以更好的显示其格式.
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- print 'DATA:', repr(data)
- print 'NORMAL:', json.dumps(data, sort_keys=True)
- print 'INDENT:', json.dumps(data, sort_keys=True, indent=2)
当indent参数是一非负整数时, 输出的结构和pprint更为接近, 在每个缩进层次上都有前导空格.
- $ python json_indent.py
- DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
- NORMAL: [{"a": "A", "b": [2, 4], "c": 3.0}]
- INDENT: [
- {
- "a": "A",
- "b": [
- 2,
- 4
- ],
- "c": 3.0
- }
- ]
像这种类型输出的数据在传输过程中需占用更多的字节, 不过, 在实际生产环境中没有必要使用缩进格式. 实际上, 你可以设置数据的分隔符来让结果更为紧凑.
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- print 'DATA:', repr(data)
- print 'repr(data) :', len(repr(data))
- print 'dumps(data) :', len(json.dumps(data))
- print 'dumps(data, indent=2) :', len(json.dumps(data, indent=2))
- print 'dumps(data, separators):', len(json.dumps(data, separators=(',',':')))
dumps()函数的separators参数是一个元组, 包含分隔列表各项和字典键值各项的字符串. 默认是(‘, ‘, ‘: ‘). 可以去掉后者中的空格, 我们可以得到较紧凑的输出.
- $ python json_compact_encoding.py
- DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
- repr(data) : 35
- dumps(data) : 35
- dumps(data, indent=2) : 76
- dumps(data, separators): 29
编码字典
JSON格式中, 字典的键被限制为字符串类型. 如果字典中的键是其他类型, 那么在编码这个对象时会产生一个TypeError异常. 一种解决这个限制的方法是, 在编码时, 使用skipkeys参数跳过所有非字符串类型的键.
- import json
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0, ('d',):'D tuple' } ]
- print 'First attempt'
- try:
- print json.dumps(data)
- except TypeError, err:
- print 'ERROR:', err
- print 'Second attempt'
- print json.dumps(data, skipkeys=True)
非字符串类型的键被忽略, 而不抛出一个异常.
- $ python json_skipkeys.py
- First attempt
- ERROR: key ('d',) is not a string
- Second attempt
- [{"a": "A", "c": 3.0, "b": [2, 4]}]
自定义类型的处理
上面所有的例子都是用了Python的内置类型作为例子, 因为他们都被json本身支持. 当然, 自定义类型也常常需要正确编码. 这里有两种情况:
第一, 对于一个类的编码:
- class MyObj(object):
- def __init__(self, s):
- self.s = s
- def __repr__(self):
- return '<MyObj(%s)>' % self.s
编码一个MyObj对象的最简单方式是定义个转换函数, 用于将位置类型转换出呢个已知类型. 你没有必要自己进行编码, 而仅需要将一个对象转换成另一个对象.
- import json
- import json_myobj
- obj = json_myobj.MyObj('instance value goes here')
- print 'First attempt'
- try:
- print json.dumps(obj)
- except TypeError, err:
- print 'ERROR:', err
- def convert_to_builtin_type(obj):
- print 'default(', repr(obj), ')'
- # Convert objects to a dictionary of their representation
- d = { '__class__':obj.__class__.__name__,
- '__module__':obj.__module__,
- }
- d.update(obj.__dict__)
- return d
- print 'With default'
- print json.dumps(obj, default=convert_to_builtin_type)
在convert_to_builtin_type()函数中, 不被json识别的类对象被转换成一个包含足够能重建这个对象的字典信息.
- $ python json_dump_default.py
- First attempt
- ERROR: <MyObj(instance value goes here)> is not JSON serializable
- With default
- default( <MyObj(instance value goes here)> )
- {"s": "instance value goes here", "__module__": "json_myobj", "__class__": "MyObj"}
为了能解码结果数据并创建一个MyObj实例, 我们需要配合解码器以便可以从模块中导入类并创建实例. 我们在loads()函数中使用object_hook参数.
在输入数据流中, 对于解码获得的每个字典都会调用object_hook, 将这个字典转换成其他类型的对象. hook函数返回的是调用程序所需要的对象, 而不是字典.
- import json
- def dict_to_object(d):
- if '__class__' in d:
- class_name = d.pop('__class__')
- module_name = d.pop('__module__')
- module = __import__(module_name)
- print 'MODULE:', module
- class_ = getattr(module, class_name)
- print 'CLASS:', class_
- args = dict( (key.encode('ascii'), value) for key, value in d.items())
- print 'INSTANCE ARGS:', args
- inst = class_(**args)
- else:
- inst = d
- return inst
- encoded_object = '[{"s": "instance value goes here", "__module__": "json_myobj", "__class__": "MyObj"}]'
- myobj_instance = json.loads(encoded_object, object_hook=dict_to_object)
- print myobj_instance
由于json将字符串值转换成unicode对象, 所以我们需要将作为类构造器的参数重新编码为ASCII字符串.
- $ python json_load_object_hook.py
- MODULE: <module 'json_myobj' from '/Users/dhellmann/Documents/PyMOTW/src/PyMOTW/json/json_myobj.pyc'>
- CLASS: <class 'json_myobj.MyObj'>
- INSTANCE ARGS: {'s': u'instance value goes here'}
- [<MyObj(instance value goes here)>]
对于内置类型也都有类似的hooks, 如整型(parse_int), 浮点型(parse_float), 常量(parse_constant).
编码和解码类
除了上述的这些函数外, json模块还提供了编码和解码类. 直接使用这些类, 你可以访问到额外的API接口或者定制创建它的子类.
JSONEncoder提供了一个产生编码数据”块”的的迭代接口, 这在写入一个文件或网络sockets时(不需要在内存中完整表示整个数据)是非常方便的,
- import json
- encoder = json.JSONEncoder()
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- for part in encoder.iterencode(data):
- print 'PART:', part
正如你看到的, 数据是以逻辑单位形式输出的, 而不是按照数据长度输出.
- $ python json_encoder_iterable.py
- PART: [
- PART: {
- PART: "a"
- PART: :
- PART: "A"
- PART: ,
- PART: "c"
- PART: :
- PART: 3.0
- PART: ,
- PART: "b"
- PART: :
- PART: [
- PART: 2
- PART: ,
- PART: 4
- PART: ]
- PART: }
- PART: ]
encode()方法基本上等价于’‘.join(encoder.iterencode()), 只是多了些附加错误检查.
为了能够编码任何类型的对象, 我们可以编写一类似于上述的convert_to_builtin_type()函数去重载default()方法.
- import json
- import json_myobj
- class MyEncoder(json.JSONEncoder):
- def default(self, obj):
- print 'default(', repr(obj), ')'
- # Convert objects to a dictionary of their representation
- d = { '__class__':obj.__class__.__name__,
- '__module__':obj.__module__,
- }
- d.update(obj.__dict__)
- return d
- obj = json_myobj.MyObj('internal data')
- print obj
- print MyEncoder().encode(obj)
这里输出的结果是和先前的实现一致的.
- $ python json_encoder_default.py
- <MyObj(internal data)>
- default( <MyObj(internal data)> )
- {"s": "internal data", "__module__": "json_myobj", "__class__": "MyObj"}
解码后将字典转换成一个对象, 在先前实现的基础上稍作修改即可.
- import json
- class MyDecoder(json.JSONDecoder):
- def __init__(self):
- json.JSONDecoder.__init__(self, object_hook=self.dict_to_object)
- def dict_to_object(self, d):
- if '__class__' in d:
- class_name = d.pop('__class__')
- module_name = d.pop('__module__')
- module = __import__(module_name)
- print 'MODULE:', module
- class_ = getattr(module, class_name)
- print 'CLASS:', class_
- args = dict( (key.encode('ascii'), value) for key, value in d.items())
- print 'INSTANCE ARGS:', args
- inst = class_(**args)
- else:
- inst = d
- return inst
- encoded_object = '[{"s": "instance value goes here", "__module__": "json_myobj", "__class__": "MyObj"}]'
- myobj_instance = MyDecoder().decode(encoded_object)
- print myobj_instance
输出结果也是和先前例子中输出的一样.
- $ python json_decoder_object_hook.py
- MODULE: <module 'json_myobj' from '/Users/dhellmann/Documents/PyMOTW/src/PyMOTW/json/json_myobj.pyc'>
- CLASS: <class 'json_myobj.MyObj'>
- INSTANCE ARGS: {'s': u'instance value goes here'}
- [<MyObj(instance value goes here)>]
流和文件的处理
到目前为止的所有例子, 我们都假设待编码的数据都是一次性完整加载到内存中的. 但对于大型数据结构来说, 将编码数据直接写入一个类文件对象, 可能会更好. load()和dump()函数可以接收一个用于读或写的类文件对象的引用.
- import json
- import tempfile
- data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
- f = tempfile.NamedTemporaryFile(mode='w+')
- json.dump(data, f)
- f.flush()
- print open(f.name, 'r').read()
对于socket来说, 也和正常文件句柄类似.
- $ python json_dump_file.py
- [{"a": "A", "c": 3.0, "b": [2, 4]}]
虽然一次性读取部分数据不是很好, 但是load()函数仍然提供了从流数据输入中封装生成对象的功能.
- import json
- import tempfile
- f = tempfile.NamedTemporaryFile(mode='w+')
- f.write('[{"a": "A", "c": 3.0, "b": [2, 4]}]')
- f.flush()
- f.seek(0)
- print json.load(f)
- $ python json_load_file.py
- [{u'a': u'A', u'c': 3.0, u'b': [2, 4]}]
混合数据流
JSONDecoder包含了raw_decode()方法, 用于解码在很多数据组成的数据结构, 例如包含多余文本的JSON数据. 返回的值是从输入数据中解码获得的对象, 数据中的index表示解码对象结束时所在的位置.
- import json
- decoder = json.JSONDecoder()
- def get_decoded_and_remainder(input_data):
- obj, end = decoder.raw_decode(input_data)
- remaining = input_data[end:]
- return (obj, end, remaining)
- encoded_object = '[{"a": "A", "c": 3.0, "b": [2, 4]}]'
- extra_text = 'This text is not JSON.'
- print 'JSON first:'
- obj, end, remaining = get_decoded_and_remainder(' '.join([encoded_object, extra_text]))
- print 'Object :', obj
- print 'End of parsed input :', end
- print 'Remaining text :', repr(remaining)
- print 'JSON embedded:'
- try:
- obj, end, remaining = get_decoded_and_remainder(
- ' '.join([extra_text, encoded_object, extra_text])
- )
- except ValueError, err:
- print 'ERROR:', err
不幸的是, 这仅仅在对象出现在输入流的开始处才有效.
- $ python json_mixed_data.py
- JSON first:
- Object : [{u'a': u'A', u'c': 3.0, u'b': [2, 4]}]
- End of parsed input : 35
- Remaining text : ' This text is not JSON.'
- JSON embedded:
- ERROR: No JSON object could be decoded