阿小信大人的头像
Talk is cheap. Show me the code. Linus Torvalds

json处理小技巧2014-09-02 06:44

首先想说json的正确语法是没有单引号存在的哦。

在Python中最常用到的json处理函数通常是json.dumps()json.loads(),他们和json.dump()json.load()的区别在于后者是对一个类文件对象(如StringIO)进行写入/读取,而前者是对字符串进行读写,参数都一样。

load()loads()没什么太多可说的,加载json为Python对象,有一个很有用的参数object_pairs_hook。loads后是无法保证json_data原始顺序的,如果想要保留原有的顺序,那么就需要用到object_pairs_hook

一个荔枝:

In [1]: from collections import OrderedDict

In [2]: import json

In [3]: json_data = '{"q": 1, "r": 4, "e": 3, "w": 2}'

In [4]: json.loads(json_data)
Out[4]: {u'e': 3, u'q': 1, u'r': 4, u'w': 2}

In [5]: json.loads(json_data, object_pairs_hook=OrderedDict)
Out[5]: OrderedDict([(u'q', 1), (u'r', 4), (u'e', 3), (u'w', 2)])

dump()dumps()是将Python对象序列化为json

dump[s]提供了很多好用的参数,其中常用参数有:

skipkeys: 默认为False,作用是判断字典的key,如果key不是基本类型(str, unicode, int, long, float, bool, None),在dumps的时候就会报TypeError错。若为True,那么遇到不合法key的时候就会跳过TA。

In [24]: data = {1: 1, 'a': 'a', None: None, (0, 0): 'bad'}

In [25]: json.dumps(data, skipkeys=True)
Out[25]: '{"a": "a", "1": 1, "null": null}'

In [26]: json.dumps(data)
#TypeError: keys must be a string

ensure_ascii: 默认为True,所有的非ascii字符在输出时都会被转义为\uxxxx的序列,返回的对象是一个只由ascii字符组成的str类型,为False时不会进行转义输出,反回的对象是个unicode。(这个参数对包含中文的json输出时非常有用)

In [28]: data = {u'我': u'是', u'美': u'女'}

In [29]: json.dumps(data)
Out[29]: '{"\\u6211": "\\u662f", "\\u7f8e": "\\u5973"}'

In [30]: json.dumps(data, ensure_ascii=False)
Out[30]: u'{"\u6211": "\u662f", "\u7f8e": "\u5973"}'

In [31]: print json.dumps(data)
{"\u6211": "\u662f", "\u7f8e": "\u5973"}

In [32]: print json.dumps(data, ensure_ascii=False)
{"我": "是", "美": "女"}

indent: 设置对json进行pretty-printed的缩进空格数

In [37]: print json.dumps(data, ensure_ascii=False, indent=0)
{
"我": "是",
"美": "女"
}

In [38]: print json.dumps(data, ensure_ascii=False, indent=4)
{
    "我": "是",
    "美": "女"
}

separators: 默认值为(', ', ': '),第一个元素为item的分隔符,第二个是key和value的分隔符。separators=(',', ':')可以用来去掉json中的空格来压缩json

In [42]: print json.dumps(data, ensure_ascii=False, separators=(',', ':'))
{"我":"是","美":"女"}

In [43]: print json.dumps(data, ensure_ascii=False)
{"我": "是", "美": "女"}

In [44]: print json.dumps(data, ensure_ascii=False, separators=('+', '-'))
{"我"-"是"+"美"-"女"}

sort_key: 默认是False,即不对进行排序操作

In [51]: data = {'q': 1, 'w': 2, 'e': 3, 'r': 4}

In [52]: print json.dumps(data, ensure_ascii=False, sort_keys=False)
{"q": 1, "r": 4, "e": 3, "w": 2}

In [53]: print json.dumps(data, ensure_ascii=False, sort_keys=True)
{"e": 3, "q": 1, "r": 4, "w": 2}

处理中文json时,要想不每次都给一堆重复的参数,可以用partial

import json
from functools import partial
json_dumps = partial(json.dumps, ensure_ascii=False, sort_keys=True)

Test:

In [64]: json_dumps("呵呵")
Out[64]: '"\xe5\x91\xb5\xe5\x91\xb5"'   # unicode

In [65]: json.dumps("呵呵")
Out[65]: '"\\u5475\\u5475"'   # ascii

In [66]: json_dumps("呵呵").decode('utf-8')  # unicode -> utf-8
Out[66]: u'"\u5475\u5475"'

In [67]: json.dumps("呵呵").decode('utf-8')  # ascii -> utf-8
Out[67]: u'"\\u5475\\u5475"'

In [68]: print json_dumps("呵呵").decode('utf-8')
"呵呵"

In [69]: print json.dumps("呵呵").decode('utf-8')      # 让大家苦恼的大概就是这个,因为她原本就不是unicode
"\u5475\u5475"

Python也有命令行里面格式化显示json的模块json.tool

> cat data.json
{"爱": "我", "中": "华"}
> cat data.json| python -m json.tool
{
    "\u4e2d": "\u534e",
    "\u7231": "\u6211"
}

好像有什么不对劲?因为json.tool在实现的时候ensure_ascii为True,让我们用Python来自己实现一个更好的Unix filter。

filter.py

    import json
    import fileinput
    for l in fileinput.input():
        print(json.dumps(json.loads(l), ensure_ascii=False).encode('utf-8'))

只需要写上面那 4 行代码,就可以这样使用它:

> python filter.py data.json
{"爱": "我", "中": "华"}
> cat data.json| python filter.py
{"爱": "我", "中": "华"}

nice!

最后为大家介绍一个处理json的超级命令行大杀器:jq

jq: http://stedolan.github.io/jq/ 就是专门为处理 JSON 而设计的小工具。

举几个例子:

  1. pretty print

    echo '["我", "你"]'|jq '.' [ "我", "你" ]

来个大的:

> curl -s http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?apikey=7waqfqbprs7pajbz28mqf6vz| jq '.'
  1. 上面提到的 filter.py

    echo '["\u4f60\u597d"]' | jq -c . ["你好"]

  2. 从 JSON 里提取一些字段拼接出一个字符串 > cat data.json [ { "id": "771324839", "title": "Jurassic World", "year": 2015, "mpaa_rating": "PG-13", "runtime": 123, "critics_consensus": "", "release_dates": { "theater": "2015-06-12" } }, { "id": "771324840", "title": "S.P.L-2", "year": 2015, "mpaa_rating": "PG-13", "runtime": 121, "critics_consensus": "", "release_dates": { "theater": "2015-06-18" } } ]

    cat data.json| jq -r '.[0]| .title + " @ " + .release_dates.theater' Jurassic World @ 2015-06-12 cat data.json| jq -r '.[0,1]| .title + " @ " + .release_dates.theater' Jurassic World @ 2015-06-12 S.P.L-2 @ 2015-06-18

最后,在Flask中直接return一个dumps的json字符串其实这样并不合理,因为这样返回的content-type是text/html,而json的content-type应该是application/json

有两种方法在flask中返回正确json,一种是使用flask的jsonify,他会在dumps的时候自动为你加上content-type,或者手动的显式在response中指定mimetype

from flask import jsonify

@router
def view():
    return jsonify(*arg, **kwargs)
    # or return Response(response=json_data, mimetype="application/json")

用jsonify很省事,但是在有中文的时候就坑爹了,可以在app的配置中设置app.config['JSON_AS_ASCII'] = False来输出unicode

如果您觉得从我的分享中得到了帮助,并且希望我的博客持续发展下去,请点击支付宝捐赠,谢谢!

若非特别声明,文章均为阿小信的个人笔记,转载请注明出处。文章如有侵权内容,请联系我,我会及时删除。

#Python#  
分享到:
阅读[7634] 评论[0]

你可能也感兴趣的文章推荐

本文最近访客

网友35.*.*.208[美国]2018-01-20 01:21
网友5.*.*.2[俄罗斯]2018-01-20 00:46
网友42.*.*.166[郑州]2018-01-20 00:12
网友5.*.*.17[俄罗斯]2018-01-19 23:45

发表评论