PythonプログラミングにおけるJSONモジュールの使用を深く解析する

6438 ワード

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にマッピングされます.次の例では、エンコードされた文字列の効果を示します.

>>> json.dumps(False)
'false'
>>> d = {'a': True,
...   'b': 'Hello',
...   'c': None}
>>> json.dumps(d)
'{"b": "Hello", "c": null, "a": true}'
>>>

JSON復号後のデータをチェックしてみると、特にデータのネストされた構造階層が深い場合や、大量のフィールドが含まれている場合、簡単な印刷で構造を特定するのは難しいことが多い.この問題を解決するために,通常のprint()関数の代わりにpprintモジュールのpprint()関数を用いることが考えられる.keyのアルファベット順に出力され、より美しい方法で出力されます.次の例では、Twitterで検索結果をきれいに印刷する方法を示します.

>>> 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の順序を保持する方法を示す例を次に示します.

>>> 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オブジェクトに変換します.

>>> 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復号後の辞書を1つのパラメータとして__に渡すinit__() . 次に、インスタンス辞書として直接使用するなど、好きなように使用することができます.
JSONをエンコードするときに、いくつかの選択肢があります.きれいなフォーマット文字列を取得して出力したい場合は、jsonを使用します.dumps()のindentパラメータ.出力はpprint()関数と同様になります.例:

>>> 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でシーケンス化されるわけではありません.例:

>>> 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 "", line 1, in 
  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=""> is not JSON serializable
>>>

オブジェクトインスタンスをシーケンス化したい場合は、入力がインスタンスであり、シーケンス化可能な辞書を返す関数を提供します.例:

def serialize_instance(obj):
  d = { '__classname__' : type(obj).__name__ }
  d.update(vars(obj))
  return d

このインスタンスを逆に取得したい場合は、次のようにします.

# 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


これらの関数の使用例を次に示します.

>>> 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="">
>>> a.x
2
>>> a.y
3
>>>

jsonモジュールには、NaNなどのより低いレベルの数値、特殊な値の解析を制御する他の選択肢がたくさんあります.詳細については、公式ドキュメントを参照してください.