当前位置: 移动技术网 > IT编程>脚本编程>Python > Python3标准库:json JavaScript对象记法

Python3标准库:json JavaScript对象记法

2020年04月15日  | 移动技术网IT编程  | 我要评论

云南省妇幼保健院,媒体,舞钢信息网

1. json javascript对象记法

json模块提供了一个与pickle类似的api,可以行化表示,被称为javascript对象记法(javascript object notation,json)。不同于pickle,json有一个优点,它有多种语言的实现(特别是javascript)。json对于restapi中web服务器和客户之间的通信使用最广泛,不过也可以用于满足其他应用间的通信需求。

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

默认的,编码器理解python的一些内置类型(即str、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()输出。

编码然后再重新解码时,可能不会得到完全相同的对象类型。

import json

data = [{'a': 'a', 'b': (2, 4), 'c': 3.0}]
print('data   :', data)

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']))

具体的,元组会变成列表。

1.2 人类可读和紧凑输出

json优于pickle的另一个好处是,json会生成人类可读的结果。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输出。

对于高度嵌套的数据结构,还可以指定一个缩进(indent)值来得到格式美观的输出。

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))

当缩进是一个非负整数时,输出更类似于pprint的输出,数据结构中每一级的前导空格与缩进级别匹配。

这种详细输出会增加传输等量数据所需的字节数,所以生产环境中往往不使用这种输出。实际上,可以调整编码输出中分隔数据的设置,从而使其比默认格式更紧凑。

import json

data = [{'a': 'a', 'b': (2, 4), 'c': 3.0}]
print('data:', repr(data))

print('repr(data)             :', len(repr(data)))

plain_dump = json.dumps(data)
print('dumps(data)            :', len(plain_dump))

small_indent = json.dumps(data, indent=2)
print('dumps(data, indent=2)  :', len(small_indent))

with_separators = json.dumps(data, separators=(',', ':'))
print('dumps(data, separators):', len(with_separators))

dumps()的separators参数应当是一个元组,其中包含用来分隔列表中各项的字符串,以及分隔字典中键和值的字符串。默认为(',',':')。通过去除空白符,可以生成一个更为紧凑的输出。

1.3 编码字典

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 as err:
    print('error:', err)

print()
print('second attempt')
print(json.dumps(data, skipkeys=true))

这里不会产生一个异常,而是会忽略非串的键。

1.4 处理定制类型

目前为止,所有例子都使用python的内置类型,因为这些类型得到了json的内置支持。通常还需要对定制类编码,有两种办法可以做到。假设以下代码清单中的类需要进行编码。

import json
class myobj: def __init__(self, s): self.s = s def __repr__(self): return '<myobj({})>'.format(self.s)

要对myobj实例编码,一个简单的方法是定义一个函数,将未知类型转换为已知类型。这个函数并不需要具体完成编码,它只是将一个类型的对象转换为另一个类型。

import json
class myobj:

    def __init__(self, s):
        self.s = s

    def __repr__(self):
        return '<myobj({})>'.format(self.s)

obj = myobj('instance value goes here')

print('first attempt')
try:
    print(json.dumps(obj))
except typeerror as 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()
print('with default')
print(json.dumps(obj, default=convert_to_builtin_type))

在convert_to_builtin_ type()中,json无法识别的类实例会被转换为字典,其中包含足够多的信息,如果程序能访问这个处理所需的python模块,就能利用这些信息重新创建对象。

要对结果解码并创建一个myobj()实例,可以使用loads()的objecthook参数关联解码器,从而可以从模块导入这个类,并将该类用来创建实例。对于从到来数据流解码的各个字典,都会调用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.__name__)
        class_ = getattr(module, class_name)
        print('class:', class_)
        args = {
            key: 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串。

内置类型也有类似的hook,如整数(parse_int)、浮点数(parse_float)和常量(parse_constant)。

1.5 编码器和解码器类

除了之前介绍的便利函数,json模块还提供了一些类来完成编码和解码。直接使用这些类可以访问另外的api来定制其行为。 

jsonencoder使用一个iterable接口生成编码数据“块”,从而更容易将其写至文件或网络套接字,而不必在内存中表示完整的数据结构。

import json

encoder = json.jsonencoder()
data = [{'a': 'a', 'b': (2, 4), 'c': 3.0}]

for part in encoder.iterencode(data):
    print('part:', part)

输出按逻辑单元输出,而不是根据某个大小值。

encode()方法基本上等价于''.join(encoder.iterencode()),只不过之前会做一些额外的错误检查。
要对任意的对象编码,需要用一个实现覆盖default()方法,这个实现类似于convert_to _builtin_type()中的实现。

import json

class myobj:

    def __init__(self, s):
        self.s = s

    def __repr__(self):
        return '<myobj({})>'.format(self.s)

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 = myobj('internal data')
print(obj)
print(myencoder().encode(obj))

输出与前一个实现的输出相同。

这里要解码文本,然后将字典转换为一个对象,与前面的实现相比,这需要多做一些工作,不过不算太多。

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.__name__)
            class_ = getattr(module, class_name)
            print('class:', class_)
            args = {
                key: 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)

输出与前面的例子相同。

1.6 处理流和文件

目前为止,所有例子都假设整个数据结构的编码版本可以一次完全放在内存中。对于很大的数据结构,更合适的做法可能是将编码直接写至一个类似文件的对象。便利函数load()和dump()会接收一个类似文件对象的引用用于读写。

import io
import json

data = [{'a': 'a', 'b': (2, 4), 'c': 3.0}]

f = io.stringio()
json.dump(data, f)

print(f.getvalue())

类似于这个例子中使用的stringio缓冲区,也可以使用套接字或常规的文件句柄。

尽管没有优化,即一次只读取数据的一部分,但load()函数还提供了一个好处,它封装了从流输入生成对象的逻辑。

import io
import json

f = io.stringio('[{"a": "a", "c": 3.0, "b": [2, 4]}]')
print(json.load(f))

类似于dump(),任何类似文件对象都可以被传递到load()。

1.7 混合数据流

js0ndecoder包含一个raw_decode()方法,如果一个数据结构后面跟有更多数据,如带尾部文本的json数据,则可以用这个方法完成解码。返回值是对输入数据解码创建的对象,以及该数据的一个索引(指示在哪里结束解码)。

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:')
data = ' '.join([encoded_object, extra_text])
obj, end, remaining = get_decoded_and_remainder(data)

print('object              :', obj)
print('end of parsed input :', end)
print('remaining text      :', repr(remaining))

print()
print('json embedded:')
try:
    data = ' '.join([extra_text, encoded_object, extra_text])
    obj, end, remaining = get_decoded_and_remainder(data)
except valueerror as err:
    print('error:', err)

遗憾的是,这种做法只适用于对象出现在输入起始位置的情况。

 

1.8 命令行上处理json

json.tool模块实现了一个命令行程序来重新格式化json数据,使数据更易读。

[{"a": "a", "c": 3.0, "b": [2, 4]}]

输入文件example.json包含一个映射,其中键采用字母表顺序。第一个例子显示了按顺序重新格式化的数据,第二个例子使用了--sort-keys在打印输出之前先对映射键排序。

[
    {
        "a": "a",
        "c": 3.0,
        "b": [
            2,
            4
        ]
    }
]

 

[
    {
        "a": "a",
        "b": [
            2,
            4
        ],
        "c": 3.0
    }
]

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网