json: JavaScript Object Notation 是 javascript 的子集合,是一種輕量級的資料交換格式,比 XML 簡單,也容易閱讀及編寫,處理過程分為序列化及反序列化兩個部分,分別是 Marshalling/Encoder 及 Unmarshalling/Decoder。
Marshalling/Encoder 就是將 python 的資料物件轉換為 json 字串,使用 json.dumps
Unmarshalling/Decoder 是將 json 字串轉換為 python 物件,使用 json.loads
使用 dumps, loads 的簡單範例
這是 python 的 dict 資料型別,因為 dict 沒有字串的表示方式,透過 repr 將 dict 轉換為可以列印的資料
json_obj = {
'name' : 'Apple',
'shares' : 100,
'price' : 542.1
}
logging.info( "json_obj type = "+str(type(json_obj))+ ", data =" + repr(json_obj) )
dict 可以透過 json.dumps 轉換為 json 字串
json_string = json.dumps(json_obj)
logging.info( "json_string type="+ str(type(json_string))+ ", data=" + json_string )
再透過 json.loads 將 json 字串轉換為 dict
json_object = json.loads(json_string)
logging.info( "json_object type="+str(type(json_object))+", data="+repr(json_object) )
測試結果
json_obj type = <class 'dict'>, data ={'name': 'Apple', 'shares': 100, 'price': 542.1}
json_string type=<class 'str'>, data={"name": "Apple", "shares": 100, "price": 542.1}
json_object type=<class 'dict'>, data={'name': 'Apple', 'shares': 100, 'price': 542.1}
由 python 資料轉換為 json 的對照表為
python | json |
---|---|
dict | object |
list, tuple | array |
str, unicode | string |
int, long, float | number |
True | true |
False | false |
None | null |
由 json 轉換為 python 資料的對照表為
json | python |
---|---|
object | dict |
array | list |
string | unicode |
number(int) | int,long |
number(real) | float |
true | True |
false | False |
null | None |
pprint 及 dumps 的 indent, sort_keys 參數
如果 json 的字串比較長,列印到 console 時,可能會比較難查閱需要的資料,這時候有兩種方式可以處理
使用 pprint 可以直接用 pprint(json_obj)
from pprint imort pprint
json_obj = {
'name' : 'Apple',
'shares' : 100,
'price' : 542.1
}
logging.info( "json_obj type = "+str(type(json_obj))+ ", data =" + repr(json_obj) )
pprint(json_obj)
執行結果為
{'name': 'Apple', 'price': 542.1, 'shares': 100}
在 dumps 加上 indent 參數
json_string = json.dumps(json_obj, indent=4)
執行結果為
json_string type=<class 'str'>, data={
"name": "Apple",
"shares": 100,
"price": 542.1
}
在 dumps 加上 sort_keys 可在轉換 json 時,同時進行 key 的排序
json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
執行結果
{"a": 0, "b": 0, "c": 0}
loads 的 object_pairs_hook
與 object_hook
參數
通常 json 會轉換為 python 的 dict 及 list,可使用 object_pairs_hook
,將 json 轉換為 OrderedDict
s = '{"name": "ACME", "shares": 50, "price": 490.1}'
from collections import OrderedDict
data = json.loads(s, object_pairs_hook=OrderedDict)
logging.info( "json_object type="+str(type(data))+", data="+repr(data) )
執行結果
json_object type=<class 'collections.OrderedDict'>, data=OrderedDict([('name', 'ACME'), ('shares', 50), ('price', 490.1)])
也可以用 object_hook
將 json 轉換為 python 物件
class JSONObject:
def __init__(self, d):
self.__dict__ = d
data2 = json.loads(s, object_hook=JSONObject)
logging.info( "json_object type="+str(type(data2))+", name="+data2.name+", shares="+str(data2.shares)+", price="+str(data2.price) )
執行結果
json_object type=<class '__main__.JSONObject'>, name=ACME, shares=50, price=490.1
skipkeys
在 dumps 如果遇到 key 不是字串時,會發生 TypeError,可使用 skipkeys 略過無法處理的 key
data = { 'b' : 789 , 'c' : 456 ,( 1 , 2 ): 123 }
print( json.dumps(data,skipkeys = True) )
執行結果
{"b": 789, "c": 456}
自訂 python 物件的 轉換函數
如果是自訂的 python 類別,通常是無法直接透過 dumps 序列化,會發生 error
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(2, 3)
print(p)
json.dumps(p)
執行結果
TypeError: Object of type 'Point' is not JSON serializable
解決方式:
首先定義兩個獨立的 util function,object2dict 是將物件轉換為 dict,dict2object 則是將 dict 轉換為 object,在 dict2object 裡面會使用 dict 的 __module__
及 __class__
這兩個資料,用來正確找到 class 的定義
def object2dict(obj):
d = {
'__class__': obj.__class__.__name__,
'__module__': obj.__module__
}
# d.update( vars(obj) )
d.update(obj.__dict__)
return d
def dict2object(d):
if '__class__' in d:
module_name = d.pop( '__module__' )
class_name = d.pop( '__class__' )
logging.debug("module_name="+str(module_name)+", class_name="+class_name)
# # from A import B
import importlib
objmodule = importlib.import_module(module_name)
cls = getattr(objmodule, class_name)
# objmodule = __import__(module_name)
# cls = getattr(objmodule, class_name)
# # use class directly
# cls = classes[class_name]
# # Make instance without calling __init__
obj = cls.__new__(cls)
for key, value in d.items():
setattr(obj, key, value)
return obj
else:
inst = d
return inst
如果另外有一個類別定義在 test.point.py
裡面
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__( self ):
return 'Point Object x : %d , y : %d' % ( self.x, self.y)
透過 object2dict 及 dict2object 就可以協助進行物件與 json 的轉換
from test import point
p = point.Point(2, 3)
logging.info(p)
json_str = json.dumps(p, default=object2dict)
logging.info(json_str)
o = json.loads(json_str, object_hook=dict2object)
logging.info( "json_object type="+str(type(o))+", data="+repr(o) )
執行結果
{"__class__": "Point", "__module__": "test.point", "x": 2, "y": 3}
module_name=test.point, class_name=Point
2018-09-03 16:01:00,274 INFO {95322,MainProcess}
json_object type=<class 'test.point.Point'>, data=Point Object x : 2 , y : 3
有時會遇到 list of Point,這時需要修改 object2dict
讓他可以處理 obj 是 list 的狀況
def object2dict(obj):
d = {}
if isinstance(obj, list):
return json.dumps(obj, default=object2dict)
else:
# d = {
# '__class__': obj.__class__.__name__,
# '__module__': obj.__module__
# }
d['__class__'] = obj.__class__.__name__
d['__module__'] = obj.__module__
# d.update( vars(obj) )
d.update(obj.__dict__)
return d
References
20.2. json — JSON encoder and decoder
沒有留言:
張貼留言