============================ 6.2 读写JSONæ•°æ® ============================ ---------- 问题 ---------- ä½ æƒ³è¯»å†™JSON(JavaScript Object Notation)ç¼–ç æ ¼å¼çš„æ•°æ®ã€‚ ---------- 解决方案 ---------- ``json`` æ¨¡å—æä¾›äº†ä¸€ç§å¾ˆç®€å•çš„æ–¹å¼æ¥ç¼–ç 和解ç JSONæ•°æ®ã€‚ å…¶ä¸ä¸¤ä¸ªä¸»è¦çš„函数是 ``json.dumps()`` å’Œ ``json.loads()`` , è¦æ¯”å…¶ä»–åºåˆ—化函数库如pickle的接å£å°‘得多。 䏋颿¼”示如何将一个Pythonæ•°æ®ç»“构转æ¢ä¸ºJSON: .. code-block:: python import json data = { 'name' : 'ACME', 'shares' : 100, 'price' : 542.23 } json_str = json.dumps(data) 䏋颿¼”示如何将一个JSONç¼–ç çš„å—符串转æ¢å›žä¸€ä¸ªPythonæ•°æ®ç»“构: .. code-block:: python data = json.loads(json_str) å¦‚æžœä½ è¦å¤„ç†çš„æ˜¯æ–‡ä»¶è€Œä¸æ˜¯å—ç¬¦ä¸²ï¼Œä½ å¯ä»¥ä½¿ç”¨ ``json.dump()`` å’Œ ``json.load()`` æ¥ç¼–ç 和解ç JSONæ•°æ®ã€‚例如: .. code-block:: python # Writing JSON data with open('data.json', 'w') as f: json.dump(data, f) # Reading data back with open('data.json', 'r') as f: data = json.load(f) ---------- 讨论 ---------- JSONç¼–ç æ”¯æŒçš„基本数æ®ç±»åž‹ä¸º ``None`` , ``bool`` , ``int`` , ``float`` å’Œ ``str`` , 以åŠåŒ…å«è¿™äº›ç±»åž‹æ•°æ®çš„lists,tupleså’Œdictionaries。 对于dictionaries,keyséœ€è¦æ˜¯å—符串类型(å—å…¸ä¸ä»»ä½•éžå—符串类型的keyåœ¨ç¼–ç æ—¶ä¼šå…ˆè½¬æ¢ä¸ºå—符串)。 为了éµå¾ªJSONè§„èŒƒï¼Œä½ åº”è¯¥åªç¼–ç Pythonçš„listså’Œdictionaries。 而且,在web应用程åºä¸ï¼Œé¡¶å±‚对象被编ç 为一个å—å…¸æ˜¯ä¸€ä¸ªæ ‡å‡†åšæ³•。 JSONç¼–ç çš„æ ¼å¼å¯¹äºŽPythonè¯æ³•è€Œå·²å‡ ä¹Žæ˜¯å®Œå…¨ä¸€æ ·çš„ï¼Œé™¤äº†ä¸€äº›å°çš„差异之外。 比如,Trueä¼šè¢«æ˜ å°„ä¸ºtrue,Falseè¢«æ˜ å°„ä¸ºfalse,而Noneä¼šè¢«æ˜ å°„ä¸ºnull。 䏋颿˜¯ä¸€ä¸ªä¾‹å,演示了编ç åŽçš„å—符串效果: .. code-block:: python >>> json.dumps(False) 'false' >>> d = {'a': True, ... 'b': 'Hello', ... 'c': None} >>> json.dumps(d) '{"b": "Hello", "c": null, "a": true}' >>> å¦‚æžœä½ è¯•ç€åŽ»æ£€æŸ¥JSONè§£ç åŽçš„æ•°æ®ï¼Œä½ 通常很难通过简å•çš„æ‰“å°æ¥ç¡®å®šå®ƒçš„结构, 特别是当数æ®çš„嵌套结构层次很深或者包å«å¤§é‡çš„å—æ®µæ—¶ã€‚ 为了解决这个问题,å¯ä»¥è€ƒè™‘使用pprint模å—çš„ ``pprint()`` 函数æ¥ä»£æ›¿æ™®é€šçš„ ``print()`` 函数。 它会按照keyçš„å—æ¯é¡ºåºå¹¶ä»¥ä¸€ç§æ›´åŠ ç¾Žè§‚çš„æ–¹å¼è¾“出。 䏋颿˜¯ä¸€ä¸ªæ¼”示如何漂亮的打å°è¾“出Twitter上æœç´¢ç»“果的例å: .. code-block:: python >>> from urllib.request import urlopen >>> import json >>> u = urlopen('http://search.twitter.com/search.json?q=python&rpp=5') >>> resp = json.loads(u.read().decode('utf-8')) >>> from pprint import pprint >>> pprint(resp) {'completed_in': 0.074, 'max_id': 264043230692245504, 'max_id_str': '264043230692245504', 'next_page': '?page=2&max_id=264043230692245504&q=python&rpp=5', 'page': 1, 'query': 'python', 'refresh_url': '?since_id=264043230692245504&q=python', 'results': [{'created_at': 'Thu, 01 Nov 2012 16:36:26 +0000', 'from_user': ... }, {'created_at': 'Thu, 01 Nov 2012 16:36:14 +0000', 'from_user': ... }, {'created_at': 'Thu, 01 Nov 2012 16:36:13 +0000', 'from_user': ... }, {'created_at': 'Thu, 01 Nov 2012 16:36:07 +0000', 'from_user': ... } {'created_at': 'Thu, 01 Nov 2012 16:36:04 +0000', 'from_user': ... }], 'results_per_page': 5, 'since_id': 0, 'since_id_str': '0'} >>> 一般æ¥è®²ï¼ŒJSONè§£ç ä¼šæ ¹æ®æä¾›çš„æ•°æ®åˆ›å»ºdicts或lists。 å¦‚æžœä½ æƒ³è¦åˆ›å»ºå…¶ä»–类型的对象,å¯ä»¥ç»™ ``json.loads()`` ä¼ é€’object_pairs_hook或object_hook傿•°ã€‚ ä¾‹å¦‚ï¼Œä¸‹é¢æ˜¯æ¼”示如何解ç JSONæ•°æ®å¹¶åœ¨ä¸€ä¸ªOrderedDictä¸ä¿ç•™å…¶é¡ºåºçš„例å: .. code-block:: python >>> s = '{"name": "ACME", "shares": 50, "price": 490.1}' >>> from collections import OrderedDict >>> data = json.loads(s, object_pairs_hook=OrderedDict) >>> data OrderedDict([('name', 'ACME'), ('shares', 50), ('price', 490.1)]) >>> 䏋颿˜¯å¦‚何将一个JSONå—典转æ¢ä¸ºä¸€ä¸ªPython对象例å: .. code-block:: python >>> class JSONObject: ... def __init__(self, d): ... self.__dict__ = d ... >>> >>> data = json.loads(s, object_hook=JSONObject) >>> data.name 'ACME' >>> data.shares 50 >>> data.price 490.1 >>> 最åŽä¸€ä¸ªä¾‹åä¸ï¼ŒJSONè§£ç åŽçš„å—典作为一个å•ä¸ªå‚æ•°ä¼ 递给 ``__init__()`` 。 ç„¶åŽï¼Œä½ å°±å¯ä»¥éšå¿ƒæ‰€æ¬²çš„使用它了,比如作为一个实例å—å…¸æ¥ç›´æŽ¥ä½¿ç”¨å®ƒã€‚ 在编ç JSON的时候,还有一些选项很有用。 å¦‚æžœä½ æƒ³èŽ·å¾—æ¼‚äº®çš„æ ¼å¼åŒ–å—符串åŽè¾“出,å¯ä»¥ä½¿ç”¨ ``json.dumps()`` çš„indent傿•°ã€‚ 它会使得输出和pprint()函数效果类似。比如: .. code-block:: python >>> print(json.dumps(data)) {"price": 542.23, "name": "ACME", "shares": 100} >>> print(json.dumps(data, indent=4)) { "price": 542.23, "name": "ACME", "shares": 100 } >>> å¯¹è±¡å®žä¾‹é€šå¸¸å¹¶ä¸æ˜¯JSONå¯åºåˆ—化的。例如: .. code-block:: python >>> class Point: ... def __init__(self, x, y): ... self.x = x ... self.y = y ... >>> p = Point(2, 3) >>> json.dumps(p) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.3/json/__init__.py", line 226, in dumps return _default_encoder.encode(obj) File "/usr/local/lib/python3.3/json/encoder.py", line 187, in encode chunks = self.iterencode(o, _one_shot=True) File "/usr/local/lib/python3.3/json/encoder.py", line 245, in iterencode return _iterencode(o, 0) File "/usr/local/lib/python3.3/json/encoder.py", line 169, in default raise TypeError(repr(o) + " is not JSON serializable") TypeError: <__main__.Point object at 0x1006f2650> is not JSON serializable >>> å¦‚æžœä½ æƒ³åºåˆ—åŒ–å¯¹è±¡å®žä¾‹ï¼Œä½ å¯ä»¥æä¾›ä¸€ä¸ªå‡½æ•°ï¼Œå®ƒçš„输入是一个实例,返回一个å¯åºåˆ—化的å—典。例如: .. code-block:: python def serialize_instance(obj): d = { '__classname__' : type(obj).__name__ } d.update(vars(obj)) return d å¦‚æžœä½ æƒ³å过æ¥èŽ·å–这个实例,å¯ä»¥è¿™æ ·åšï¼š .. code-block:: python # Dictionary mapping names to known classes classes = { 'Point' : Point } def unserialize_object(d): clsname = d.pop('__classname__', None) if clsname: cls = classes[clsname] obj = cls.__new__(cls) # Make instance without calling __init__ for key, value in d.items(): setattr(obj, key, value) return obj else: return d 䏋颿˜¯å¦‚何使用这些函数的例å: .. code-block:: python >>> p = Point(2,3) >>> s = json.dumps(p, default=serialize_instance) >>> s '{"__classname__": "Point", "y": 3, "x": 2}' >>> a = json.loads(s, object_hook=unserialize_object) >>> a <__main__.Point object at 0x1017577d0> >>> a.x 2 >>> a.y 3 >>> ``json`` 模å—è¿˜æœ‰å¾ˆå¤šå…¶ä»–é€‰é¡¹æ¥æŽ§åˆ¶æ›´ä½Žçº§åˆ«çš„æ•°å—ã€ç‰¹æ®Šå€¼å¦‚NaNç‰çš„è§£æžã€‚ å¯ä»¥å‚è€ƒå®˜æ–¹æ–‡æ¡£èŽ·å–æ›´å¤šç»†èŠ‚ã€‚