设为首页 加入收藏

TOP

Python 3 中的json模块使用(三)
2018-06-09 10:08:00 】 浏览:548
Tags:Python json 模块 使用
t['f'])
<type 'numpy.float64'>


2.6.1 自定义NaN, Infinity和-Infinity转换类型


由于标准JSON数据不支持NaN, Infinity和-Infinity, 所以parse_float并不会接收到这几个值. 当需要自定义这几个值转换的对象的时候, 就需要使用另外一个接口parse_constant. 比如下例中, 将这几个值同样转换为numpy.float64类型:
>>> def my_parse_constant(data):
...    print('%s(%s)' % (type(data), data))
...    return numpy.float64(data)
...
>>> result = json.loads('{"inf": Infinity, "nan": NaN, "ninf": -Infinity}', parse_constant=my_parse_constant)
<type 'str'>(Infinity)
<type 'str'>(NaN)
<type 'str'>(-Infinity)
>>> result['inf']
inf
>>> type(result['inf'])
<type 'numpy.float64'>


2.7 非对象顶级值


根据JSON规范, 一个JSON数据中, 可以只包含一个值, 而不是一个完整的对象. 这个值可以是一个字符串, 一个数字, 布尔值, 空值, 或者一个数组. 除了这三种JSON规范中给出的类型, 还可以是NaN, Infinity或者-Infinity:
>>> json.loads('"hello"')
'hello'
>>> json.loads('123')
123
>>> json.loads('123.34')
123.34
>>> json.loads('true')
True
>>> json.loads('false')
False
>>> print(json.loads('null'))
None
>>> json.loads('[1, 2, 3]')
[1, 2, 3]


2.8 重复键名


在同一层级JSON对象中, 不应当出现重复的键名, 不过JSON规范中没有给出这种情况的处理标准. 在json.loads中, 当JSON数据中有重复键名, 则后面的键值会覆盖前面的:
>>> json.loads('{"a": 123, "b": "ABC", "a": 321}')
{'a': 321, 'b': 'ABC'}


2.9 处理JSON数据文件


当JSON数据是保存在一个文件中的时候, json.load方法可以用来从这个文件中读取数据, 并转换为Python对象. json.load方法的第一个参数就是指向JSON数据文件的文件类型对象.


比如/tmp/data.json文件的内含如下:
{
    "a": 123,
    "b": "ABC"
}


可以使用下例中的代码来读取并转化文件中的JSON数据:
>>> with open('/tmp/data.json') as jf:
...    json.load(jf)
...
{u'a': 123, u'b': u'ABC'}


除了文件类型的对象, 只要是实现了read方法的类文件对象, 都可以作为fp参数, 比如下例中的io.StringIO:
>>> sio = io.StringIO('{"a": 123}')
>>> json.load(sio)
{'a': 123}


json.load方法的其他参数的意义和使用方法和上文中的json.loads相同, 这里不再赘述.


3 生成JSON


json.dumps方法可以将Python对象转换为一个表示JONS数据的字符串. 它的完整接口签名如下:
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)


它的第一个参数obj即为要转换的数据对象.
>>> json.dumps({'a': 123, 'b': 'ABC'})
'{"a": 123, "b": "ABC"}'


3.1 编码格式


json.dumps的ensure_ascii参数用来控制生成的JSON字符串的编码. 其默认值为True, 此时, 所有的非ASCII码字条都会转义. 如果不希望自动进行转义, 则会保持原有编码, 限UTF-8. 如下例所示:
>>> json.dumps({'数字': 123, '字符': '一二三'})
'{"\\u6570\\u5b57": 123, "\\u5b57\\u7b26": "\\u4e00\\u4e8c\\u4e09"}'
>>> json.dumps({'数字': 123, '字符': '一二三'}, ensure_ascii=False)
'{"数字": 123, "字符": "一二三"}'


3.2 数据类型转换


在默认实现中, json.dumps可以处理的Python对象, 及其所有的属性值, 类型必须为dict, list, tuple, str, float或者int. 这些类型与JSON的数据转换关系如下表:


实际转换情况如下示例:
>>> json.dumps(
...    {
...            'str': 'ABC',
...            'int': 123,
...            'float': 321.45,
...            'bool_true': True,
...            'bool_false': False,
...            'none': None,
...            'list': [1, 2, 3],
...            'tuple': [12, 34]
...    }
... )
'{"str": "ABC", "int": 123, "float": 321.45, "bool_true": true, "bool_flase": false, "none": null, "list": [1, 2, 3], "tuple": [12, 34]}'


虽然JSON标准规范不支持NaN, Infinity和-Infinity, 但是json.dumps的默认实现会将float('nan'), float('inf')和float('-inf')转换为常量NaN, Infinity, 和-Infinity. 如下例所示:
>>> json.dumps(
...    {
...            'nan': float('nan'),
...            'inf': float('inf'),
...            '-inf': float('-inf')
...    }
... )
'{"nan": NaN, "inf": Infinity, "-inf": -Infinity}'


由于这些常量可能会导致生成的JSON字符串不能被其他的JSON实现处理, 为了防止这种情况出现, 可以将json.dumps的allow_nan参数设置为True. 此时, 当处理的Python对象中出现这些值时, json.dumps方法会抛出异常.


3.3 循环引用


json.dumps方法会检查Python对象中是否有循环引用, 如果发现了循环引用, 就会抛出异常. 如下例所示:
>>> circular_obj = {}
>>> circular_obj['self'] = circular_obj
>>> circular_obj
{'self': {...}}
>>> json.dumps(circular_obj)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
ValueError: Circular reference detected


如果不希望json.dumps方法检查循环引用, 可以将参数check_circular设置为False. 但如果此时Python对象中有循环引用, 有可能发生递归嵌套过深的错误或者其他错误, 这么做是比较危险的. 如下例所示:
>>> json.dumps(circular_obj, check_circular=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
RecursionError: maximum recursion depth exceeded while encoding a JSON object


3.4 JSON字符串输出格式


json.dumps方法的indent参数可以用来控制JSON字符串的换行和缩进效果.


indent参数默认值为None. 此时, JSON字符串不会有换行和缩进效果. 如下示:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}))
{"a": 123, "b": {"x": 321, "y": "ABC"}}


当indent为0或者负数时, JSON字符会包含换行:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=-1))
{
"a": 123,
"b": {
"x": 321,
"y": "ABC"
}
}
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=0))
{
"a": 123,
"b": {
"x": 321,
"y": "ABC"
}
}


而当indent为正整数时, 除了换行, JSON还会以指定数量的空格为单位在对象层次间进行缩进:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=2))
{
  "a": 123,
  "b": {
    "x": 321,
    "y": "ABC"
  }
}


indent还可以是str, 此时, JSON会以str内容为单位进行缩进, 比如制表符\t:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent='\t'))
{
        "a": 123,
        "b": {
            "x": 321,
            "y": "ABC"
        }
}


json.dumps的另外一个参数separators可以用来设置输出的分隔符. 这个参数的值应当是一个有两个元素的tuple. 其第一个值为成员间的分隔符, 第二个值为键值之间的分隔符. 其默认值也会随上文中的indent参数影响. 当indent为None时, separators的默认值为(', ', ': '), 即分隔符后都有一个空格. 当indent不为None时, 其默认值则为(',', ':'), 即只有键值间分隔符后会有一个空格, 而元素间分隔符则不带空格, 因为此时会有换行.


separators参数的一种可能的使用场景是希望移除所有的非必要格式字符, 以此来减小JSON字符串的大小. 此时可以将separator设置为(',', ';'), 并不设置indent参数, 或者将其显式设置为None:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=None, separators=(',', ':')))
{"a":123,"b":{"x":321,"y":"ABC"}}


3.5 转换自定义Python对象


json.dumps的默认实现只能转换Dictionary类型的对象. 如果想要转换自定义对象, 需要使用default参数. 这个参数接收一个函数, 这个函数的参数是一个要转换的Python对象, 返回值是能够表示这个Python对象的Dictionary对象. default函数会从对象引用树的顶层开始, 逐层遍历整个对象引用树. 因此, 不用自己实现对象树的遍历逻辑, 只需要处理当前层次的对象. 如下例所示:
>>> class MyClass:
...    def __init__(self, x, y):
...            self.x = x
...            self.y = y
...
>>> def my_default(o):
...    if isinstance(o, MyClass):
...            print('%s.y: %s' % (type(o), o.y))
...            return {'x': o.x, 'y': o.y}
...    print(o)
...    return o
...
>>> obj = MyClass(x=MyClass(x=1, y=2), y=11)
>>> json.dumps(obj, default=my_default)
<class '__main__.MyClass'>.y: 11
<class '__main__.MyClass'>.y: 2
'{"x": {"x": 1, "y": 2}, "y": 11}'


3.6 非字符串类型键名


在Python中, 只是可哈希(hashable)的对象和数据都可以做为Dictionary对象的键, 而JSON规范中则只能使用字符串做为键名. 所以在json.dumps的实现中, 对这个规则进行了检查, 不过键名允许的范围有所扩大, str, int, float, bool和None类型的数据都可以做为键名. 不过当键名非str的情况时, 键名会转换为对应的str值. 如下例:
>>> json.dumps(
...    {
...            'str': 'str',
...            123: 123,
...            321.54: 321.54,
...            True: True,
...            False: False,
...            None: None
...    }
... )
'{"str": "str", "123": 123, "321.54": 321.54, "true": true, "false": false, "null": null}'


而当出现其他类型的键名时, 默认出抛出异常:
>>> json.dumps({(1,2): 123})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
TypeError: keys must be a string


json.dumps的skipkeys参数可以改变这个行为. 当将skipkeys设置为True时, 遇到非法的键名类型, 不会抛出异常, 而是跳过这个键名:
>>> json.dumps({(1,2): 123}, skipkeys=True)
'{}'


3.7 生成JSON文件


当需要将生成的JSON数据保存到文件时, 可以使用json.dump方法. 这个方法比json.dumps多了一个参数fp, 这个参数就是用来保存JSON数据的文件对象. 比如, 下例中的代码
>>> with open('/tmp/data.json', mode='a') as jf:
...    json.dump({'a': 123}, jf)
...


就会将JSON数据写入到/tmp/data.json文件里. 代码执行完后, 文件内容为
{"a": 123}


json.dump方法也可以接受其他类文件对象:
>>> sio = io.StringIO()
>>> json.dump({'a': 123}, sio)
>>> sio.getvalue()
'{"a": 123}'


json.dump的其他参数和json.dumps的用法相同, 这里不再赘述.


4 SON解码和编码类实现


json.loads, json.load, json.dumps和json.dump这四个方法是通过json.JSONDecoder和json.JSONEncoder这两个类来完成各自的任务的. 所以也可以直接使用这两个类来完成前文描述的功能:
>>> json.JSONDecoder().decode('{"a": 123}')
{'a': 123}
>>> json.JSONEncoder().encode({'a': 123})
'{"a": 123}'


json.loads, json.load, json.dumps和json.dump这个四个方法的参数主要都是传递给了json.JSONDecoder和json.JSONEncoder的构造方法, 所以使用这些方法可以满足绝大部分需求. 当需要自定义json.JSONDecoder和json.JSONEncoder子类的时候, 只需要将子类传递给cls参数. 同时, 这些方法都有**kw参数. 当自定义实现类的构造函数需要标准参数列表之外的新参数时, 这个参数就会将新参数传递给实现类的构造方法.


首页 上一页 1 2 3 下一页 尾页 3/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Python 字节码介绍 下一篇Java泛型实例详解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目