何謂序列化Serialization
假設在玩一個電腦遊戲, 一個變數 level=1 記錄著目前進行的關數. 這個變數是存在於Ram之中的.在程式運行中, 關數一直累進, 比如 level 會變更到第50關卡. 當程式關掉後, 再重新執行, 那麼關數level又再度回到1, 玩家必需從第一關開始玩, 這不會讓人訐譙嗎!!!
如果下次再度執行, 要從第51關卡開始, 那麼 level這個變數就必需保留起來. 怎麼保留呢, 當然, 最直覺的方法, 就是保留在硬碟或sdcard裏. 這種把變數存放在硬碟的動作, 稱為序列化.
Serialization是在Java中的稱呼. 在Python中, 英文是以Pickling代表序列化. Python提供 pickle模組支援序列化功能. pickle 是泡菜的意思, 現在知道為什麼本人都使用Serialization了吧.
dumps/loads
先看下面的互動模式, 字典 d 被dumps()到硬碟後, 傳回 p1的結果. 這時如果要把字典的結果抓回來, 就要使用 p2=pickle.loads(p1), 資料就會回復到p2裏。請注意,此時 p1是存在記憶体之中喔,所以在關掉程式後,下次直接下 p2=pickle.loads(p1),它就會靠妖p1沒定義啊。
>>> import pickle >>> d=dict(name='thomas', age=18) >>> p1=pickle.dumps(d) >>> d["name"]="tracy" >>> d {'name': 'tracy', 'age': 18} >>> p2=pickle.loads(p1) >>> p2 {'name': 'thomas', 'age': 18}
dump/load
dump()允許將資料寫入到指定的檔案中, 再由load()從指定的檔案讀取進來. 這比較適用於程式碼的撰寫. 寫入時, 模式設為 ‘wb’, 讀入時, 模式設為 ‘rb’. 寫入是一筆一筆寫入, 讀出也是一筆一筆讀出. 但若讀過頭了, 就會發出例外.
import pickle name='張無忌' d=dict(name='thomas', age=18) with open("d:/python.pkl",'wb') as file: pickle.dump(name, file) pickle.dump(d, file) with open('d:/python.pkl','rb') as file: p=pickle.load(file) print(p) p = pickle.load(file) print(p)
物件序列化
物件只會序列化物件變數, 類別變數則無法序列化
import pickle class Student: count=0 def __init__(self, name='', score=0): self.name=name; self.score=score; s1=Student('Thomas', 100) Student.count=50 with open("d:/python.pkl",'wb') as file: pickle.dump(s1, file) Student.count=51 with open('d:/python.pkl','rb') as file: s2=pickle.load(file) print('物件變數 name :', s2.name) print('物件變數 score :', s2.score) print('類別變數 count :',Student.count) 結果: 物件變數 name : Thomas 物件變數 score : 100 類別變數 count : 51
序列化為JSON格式
為什麼要序列化為JSON格式呢, 尤其本人超討厭JSON的, 總認為那是不會寫程式的人在胡搞瞎搞的. 原因在於Python序列化後, 無法讓其他程式讀取, 甚至不同版本的Python也不相容. 不過Python官方是有說啦, 保証新版一定會相容於舊版的pickle. 但眾說紛云, 也不知該聽誰的. 所以許多人就會寫成如xml之類的. 而 JSON不僅是標準格式, 且速度比XML還快. 好吧, 那就勉為其難的用一下吧.
JSON格式的寫法, 就是把pickle.dump() 改為json.dump()而以, 不過要注意的是, JSON是以純文字儲存的, 所以檔案開檔類型必需使用 ‘w’, 不是 ‘wb’
import json d=dict(name='Thomas', age=20) with open("d:/python.pkl",'w') as file: json.dump(d, file) with open('d:/python.pkl','r') as file: j=json.load(file) print(j["name"])
將上述的python.pkl使用記事本打開, 可以看到如下的明碼內容
{"name": "Thomas", "age": 20}
物件序列化為JSON格式
物件無法序列化為JSON格式, 所以唯一的辦法就是把物件變數轉化為dict, 再交由JSON存檔. 這個動作我們可以寫成todict(s)這個函數來完成, 然後在dump() 加入default=todict即可
載入時, 同樣寫一個從dict轉化成物件的方法tostudent(), 然後在load()加入參數 object_hook=tostudent
import json class Student: count=0 def __init__(self, name='', score=0): self.name=name; self.score=score; def todict(s): return {'name':s.name, 'score':s.score} s1=Student('Thomas', 100) d=dict(name='Thomas', age=20) with open("d:/python.pkl",'w') as file: json.dump(s1, file, default=todict) def tostudent(d): return Student(d['name'], d['score']) with open('d:/python.pkl','r') as file: s2=json.load(file, object_hook=tostudent) print(s2.name) print(s2.score) 結果: Thomas 100
偷懶一下
上面的todict()函數, 其實是不用寫的, 因為要把物件轉成dict, 系統早就幫我們寫好了, 只要調用 __dict__即可, 所以上面的dump可以寫成如下
with open("d:/python.pkl",'w') as file:
json.dump(s1, file, default=lambda o:o.__dict__)
也就是說, 完整代碼如下
import json class Student: count=0 def __init__(self, name='', score=0): self.name=name; self.score=score; s1=Student('Thomas', 100) d=dict(name='Thomas', age=20) with open("d:/python.pkl",'w') as file: json.dump(s1, file, default=lambda o:o.__dict__) def tostudent(d): return Student(d['name'], d['score']) with open('d:/python.pkl','r') as file: s2=json.load(file, object_hook=tostudent) print(s2.name) print(s2.score)
混合 JSON 格式
物件 : 使用 {}包含, 每個物件都有key-value, 各個物件之間使用 “,”隔開, 如
{“name”:”thomas”, “math”:100}
陣列 : 使用[] 包含, 如[10,20,30]
混合 : [{“name”:”thomas”, “math”:100}, {“name”:”john”, “math”:50}]
Python語法
下面代碼中, 需先import json. data為Python裏的List 格式, 裏面包含了二個字典
json.dumps(字典/List), 則會將字典/List轉成字串, 而且是符合JSON的格式. 因此使用foreach印出時, 是一個字元一個字元的印出
json.loads(字串), 則是將字串轉成List/字典的格式, 所以印出來的結果, 是List的格式
import json data=[{"name":"thomas", "math":100, "english":50},{"name":"john", "math":80, "english":100}] #底下是將list轉成字串, 再一個字一個字印出 str=json.dumps(data) for l in str: print(l) #底下是已轉成list, 將list印出 ls=json.loads(str) for l in ls: print(l) 結果 : [ { " n a ........................ {'name': 'thomas', 'math': 100, 'english': 50} {'name': 'john', 'math': 80, 'english': 100}
C#傳送 JSON方式
WebRequest request = WebRequest.Create("https://jsonbin.org/me/test"); request.Method = "POST"; request.ContentType = "application/json; charset=utf-8"; request.Headers.Add("authorization", "token apikey"); using (var streamWriter = new StreamWriter(request.GetRequestStream())) { string json = "{'name':'Thomas','maht':100, 'english':80}"; streamWriter.Write(json); streamWriter.Flush(); } using (var httpResponse = (HttpWebResponse)request.GetResponse()) using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { var result = streamReader.ReadToEnd(); result.Dump(); }