序列化與JSON

      在〈序列化與JSON〉中尚無留言

何謂序列化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();
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *