Python處理JSON

(若是閱讀效果不佳,可戳這裏python

概念git

序列化(Serialization):將對象的狀態信息轉換爲能夠存儲或能夠經過網絡傳輸的過程,傳輸的格式能夠是JSON、XML等。反序列化就是從存儲區域(JSON,XML)讀取反序列化對象的狀態,從新建立該對象。github

JSON(JavaScript Object Notation):一種輕量級數據交換格式,相對於XML而言更簡單,也易於閱讀和編寫,機器也方便解析和生成,Json是JavaScript中的一個子集。json

Python2.6開始加入了JSON模塊,無需另外下載,Python的Json模塊序列化與反序列化的過程分別是 encoding和 decoding數組

encoding:把一個Python對象編碼轉換成Json字符串
decoding:把Json格式字符串解碼轉換成Python對象
對於簡單數據類型(string、unicode、int、float、list、tuple、dict),能夠直接處理。網絡

json.dumps方法對簡單數據類型encoding:
import json
data = [{'a':"A",'b':(2,4),'c':3.0}]  #list對象
print "DATA:",repr(data)

data_string = json.dumps(data)
print "JSON:",data_string

輸出:數據結構

DATA: [{'a':'A','c':3.0,'b':(2,4)}] #python的dict類型的數據是沒有順序存儲的
JSON: [{"a":"A","c":3.0,"b":[2,4]}]

JSON的輸出結果與DATA很類似,除了一些微妙的變化,如python的元組類型變成了Json的數組,Python到Json的編碼轉換規則是: 函數

json.loads方法處理簡單數據類型的decoding(解碼)轉換
import json
data = [{'a':"A",'b':(2,4),'c':3.0}]  #list對象

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

輸出:大數據

ENCODED: [{"a": "A", "c": 3.0, "b": [2, 4]}]
DECODED: [{u'a': u'A', u'c': 3.0, u'b': [2, 4]}]
ORIGINAL: <type 'tuple'>
DECODED: <type 'list'>

解碼過程當中,json的數組最終轉換成了python的list,而不是最初的tuple類型,Json到Python的解碼規則是: ui

json的人文關懷

編碼後的json格式字符串緊湊的輸出,並且也沒有順序,所以dumps方法提供了一些可選的參數,讓輸出的格式提升可讀性,如sort_keys是告訴編碼器按照字典排序(a到z)輸出。

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)

輸出:

DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
JSON: [{"a": "A", "c": 3.0, "b": [2, 4]}]
SORT: [{"a": "A", "b": [2, 4], "c": 3.0}

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)

輸出:

DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
NORMAL: [{"a": "A", "b": [2, 4], "c": 3.0}]
INDENT: [
  {
    "a": "A",
    "b": [
      2,
      4
    ],
    "c": 3.0
  }
]

separators參數的做用是去掉,,:後面的空格,從上面的輸出結果都能看到", :"後面都有個空格,這都是爲了美化輸出結果的做用,可是在咱們傳輸數據的過程當中,越精簡越好,冗餘的東西所有去掉,所以就能夠加上separators參數:

import json

data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
print 'DATA:', repr(data)
print 'repr(data)             :', len(repr(data))
print 'dumps(data)            :', len(json.dumps(data))
print 'dumps(data, indent=2)  :', len(json.dumps(data, indent=2))
print 'dumps(data, separators):', len(json.dumps(data, separators=(',',':')))

輸出:

DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
repr(data)             : 35
dumps(data)            : 35
dumps(data, indent=2)  : 76
dumps(data, separators): 29

skipkeys參數,在encoding過程當中,dict對象的key只能夠是string對象,若是是其餘類型,那麼在編碼過程當中就會拋出ValueError的異常。skipkeys能夠跳過那些非string對象看成key的處理.

import json

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

try:
    print json.dumps(data)
except (TypeError, ValueError) as err:
    print 'ERROR:', err
print 
print json.dumps(data, skipkeys=True)

輸出:

ERROR: keys must be a string

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

讓json支持自定義數據類型

以上例子都是基於python的built-in類型的,對於自定義類型的數據結構,json模塊默認是無法處理的,會拋出異常:TypeError xx is not JSON serializable,此時你須要自定義一個轉換函數:

import json  

class MyObj(object):
    def __init__(self, s):
        self.s = s
    def __repr__(self):
        return '<MyObj(%s)>' % self.s

obj = .MyObj('helloworld')

try:
    print json.dumps(obj)
except TypeError, err:
    print 'ERROR:', err

#轉換函數
def convert_to_builtin_type(obj):
    print 'default(', repr(obj), ')'
    # 把MyObj對象轉換成dict類型的對象
    d = { '__class__':obj.__class__.__name__, 
          '__module__':obj.__module__,
        }
    d.update(obj.__dict__)
    return d

print json.dumps(obj, default=convert_to_builtin_type)

輸出:

ERROR: <MyObj(helloworld)> is not JSON serializable
default( <MyObj(helloworld)> )
{"s": "hellworld", "__module__": "MyObj", "__class__": "__main__"} 
#注意:這裏的class和module根據你代碼的所在文件位置不一樣而不一樣

相反,若是要把json decode 成python對象,一樣也須要自定轉換函數,傳遞給json.loads方法的object_hook參數:

#jsontest.py

import json

class MyObj(object):

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

    def __repr__(self):

        return "<MyObj(%s)>" % self.s

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

        class_ = getattr(module,class_name)

        print "CLASS",class_

        args = dict((key.encode('ascii'),value) for key,value in d.items())

        print 'INSTANCE ARGS:',args

        inst = class_(**args)
    else:
        inst = d
    return inst

encoded_object = '[{"s":"helloworld","__module__":"jsontest","__class__":"MyObj"}]'

myobj_instance = json.loads(encoded_object,object_hook=dict_to_object)
print myobj_instance

輸出:

MODULE: <module 'jsontest' from 'E:\Users\liuzhijun\workspace\python\jsontest.py'>
CLASS <class 'jsontest.MyObj'>
INSTANCE ARGS: {'s': u'helloworld'}
[<MyObj(helloworld)>]
MODULE: <module 'jsontest' from 'E:\Users\liuzhijun\workspace\python\jsontest.py'>
CLASS <class 'jsontest.MyObj'>
INSTANCE ARGS: {'s': u'helloworld'}
[<MyObj(helloworld)>]

使用Encoder與Decoder類實現json編碼的轉換

JSONEncoder有一個迭代接口iterencode(data),返回一系列編碼的數據,他的好處是能夠方便的把逐個數據寫到文件或網絡流中,而不須要一次性就把數據讀入內存.

import json

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

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

輸出:

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

encode方法等價於''.join(encoder.iterencode(),並且預先會作些錯誤檢查(好比非字符串做爲dict的key),對於自定義的對象,咱們只需從些JSONEncoder的default()方法,其實現方式與上面說起的函數convet_to_builtin_type()是相似的。

import json
import json_myobj

class MyObj(object):

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

    def __repr__(self):
        return "<MyObj(%s)>" % 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 = json_myobj.MyObj('helloworld')
print obj
print MyEncoder().encode(obj)

輸出:

<MyObj(internal data)>
default( <MyObj(internal data)> )
{"s": "helloworld", "__module__": "Myobj", "__class__": "MyObj"}

從json對Python對象的轉換:

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
            class_ = getattr(module, class_name)
            print 'CLASS:', class_
            args = dict( (key.encode('ascii'), value) for key, value in d.items())
            print 'INSTANCE ARGS:', args
            inst = class_(**args)
        else:
            inst = d
        return inst

encoded_object = '[{"s": "helloworld", "__module__": "jsontest", "__class__": "MyObj"}]'

myobj_instance = MyDecoder().decode(encoded_object)
print myobj_instance

輸出:

MODULE: <module 'jsontest' from 'E:\Users\liuzhijun\workspace\python\jsontest.py'>
CLASS: <class 'jsontest.MyObj'>
INSTANCE ARGS: {'s': u'helloworld'}
[<MyObj(helloworld)>]

json格式字符串寫入到文件流中

上面的例子都是在內存中操做的,若是對於大數據,把他編碼到一個類文件(file-like)中更合適,load()dump()方法就能夠實現這樣的功能。

import json
import tempfile

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

f = tempfile.NamedTemporaryFile(mode='w+')
json.dump(data, f)
f.flush()

print open(f.name, 'r').read()

輸出:

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

相似的:

import json
import tempfile

f = tempfile.NamedTemporaryFile(mode='w+')
f.write('[{"a": "A", "c": 3.0, "b": [2, 4]}]')
f.flush()
f.seek(0)

print json.load(f)

輸出:

[{u'a': u'A', u'c': 3.0, u'b': [2, 4]}]
相關文章
相關標籤/搜索