總目錄
What 、Why、How
What
Why
How
PHP序列化文法
PHP序列化執行個體
在JavaScript中序列化為JSON—使用json2.js
在JavaScript中序列化為JSON—使用prototype.js
PHP與JSON
json_decode函數
json_encode函數
json_decode函數執行個體
json_encode函數執行個體
實踐出真知
背景說明
前台JavaScript部分
後台PHP部分
我還有話要說
What 、Why、How
What
Ok,各位親愛的朋友,讓我們開始這個新概念的旅程,序列化這個話題可能大家以前都沒有多加關注,事情其實起源於那天我隨便翻翻PHP手冊,發現這個序列化的函數,之後閑來無聊又做一個WordPress的外掛程式,這個時候順便用了一下序列化,發現在某些場合的確非常方便。
先來解釋下序列化:簡單來說,序列化即將變數轉換成位元組流的過程。序列化的提出,有效解決了對象的儲存和傳輸的問題,舉例來說,我在JavaScript中建立了一個對象,我現在想將這個對象儲存到伺服器端的資料庫中,那麼我如何進行操作呢,這個時候往往就用到了對象的序列化。在JavaScript的序列化中不得不提JSON,JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式。易於人閱讀和編寫,同時也易於機器解析和產生。它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON採用完全獨立於語言的文字格式設定,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。這些特性使JSON成為理想的資料交換語言。
人們通常將JSON和XML進行比較,二者都是將對象扁平化(稍後我們解釋這個“扁平化”)的一種手段,XML的特點是結構嚴謹,而JSON的特點則是簡單易讀、容易使用程式進行分析,因為它能夠很簡單的將一個對象轉換為一個字元流的形式,例如如下代碼:
複製內容到剪貼簿
代碼:
{"type":"human","name":"hanguofeng","age":22}
則是一個JSON運算式,他儲存了一個對象,我們如何將它恢複為對象呢?很簡單,如下:
複製內容到剪貼簿
代碼:
var animal_str = '{"type":"human","name":"hanguofeng","age":22}';
var animal2=eval('(' + animal_str + ')');
我們通過JavaScript的求值函數,將JSON運算式進行運算,並傳回值,用以獲得一個對象,到這裡,我想你一定會和我一樣,對JSON格式的創造者的思維佩服不已吧。
本來說講序列化的,“不小心”談到JSON,並且講了這麼多,呵呵,跑題了嗎?沒有,PHP的序列化和JSON是非常像的,一個PHP的序列化運算式如下:
複製內容到剪貼簿
代碼:
a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}
他看起來結構和JSON有些類似,實際上,這個運算式是如下數組的序列化結果:
複製內容到剪貼簿
代碼:
$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age" => "20"
);
OK,上面的一些介紹只是讓你大致看到序列化和JSON是什麼樣的東西,你無須對這裡的代碼過分糾結,我們在後面會詳細講解的,下面我們來談談為什麼要使用序列化。
Why
序列化首先是作為資料轉送的方便而出現的,正如本文開始我提出的問題,我在JavaScript中建立了一個對象,我現在想將這個對象儲存到伺服器端的資料庫中,應該如何做,這其實上是一個“我如何將一個對象從瀏覽器提交到伺服器”的問題,在這個傳輸過程中,我們知道,實際上只能夠傳遞字元流,字元流是一維(扁平)的,然而很多個物件卻是多維的,如果要傳遞的對象是一個字串,那麼很簡單,我們直接將其作為傳遞的內容就可以了,如果要傳遞的對象是一個數組或者其他的結構呢,我們就需要用字元流來描述他,就比如在電話裡面,我問你的名字是什麼,你會告訴我,你的名字是張三、李四,而我問你,你的長相如何呢,你就需要用文字向我描述了,我們進行資料傳遞的媒介往往和這條電話線路一樣,只能傳遞字元流,而我們描述對象的過程,實際上就是序列化的過程。
另外,序列化也可以用於對對象的持久化儲存,也許你曾經也和我一樣,想著在資料庫的某一個欄位中儲存一個對象,現在我們可以非常簡單的做到這一點,並且,你的這個資料庫欄位不需要設定為特殊格式,設定為varchar就可以了(當然,如果對象很大,你可能需要設定為text)。
How
PHP序列化文法
好了,我想What和Why的問題你都瞭解了,本節最後我們來講點理論性強一些的內容,就是如何使用PHP序列化和反序列化資料,如何將JavaScript對象序列化(即變為JSON格式)和如何將其反序列化,最後則是如何將JSON和PHP的序列化建立關係。
PHP為我們提供了兩個函數,用來進行序列化和反序列化的操作,這兩個函數分別是:serialize()和unserialize(),他們適用於PHP4和PHP5,下面分別進行講解:
serialize()
(PHP 4, PHP 5, PECL axis2:0.1.0-0.1.1)
serialize — 獲得一個可儲存的表述值
說明
string serialize ( mixed $value )
獲得一個可儲存的表述值
本函數用於無損的儲存或者傳遞PHP變數值和結構。
如果需要將已經序列化的值轉回PHP變數,可以使用unserialize()函數。
參數
value
即被序列化的運算式。serialize()處理除資源指標之外的所有類型,你甚至可以將含有指向自身元素的數組序列化。你序列化的含有迴圈指向的數組或者對象一樣會被儲存,其他的指向則會丟失。
當序列化對象時,PHP會嘗試首先調用其成員函數__sleep()。這將允許對象在被序列化之前進行諸如最後的清理工作等。同樣地,當使用unserialize()函數將對象恢複時,會調用成員函數__wakeup()。
傳回值
返回一個可以被儲存在任何地點的包含對象的位元組流運算式的字串。
unserialize()
(PHP 4, PHP 5, PECL axis2:0.1.0-0.1.1)
unserialize — 從一個已儲存的運算式中獲得一個PHP變數值
說明
mixed unserialize ( string $str )
unserialize()擷取一個簡單類型的序列化變數並將其轉換回PHP變數值。
參數
str
序列化後的字串
如果被反序列化的變數是一個對象,則成功恢複該對象的結構後,PHP將自動嘗試執行該對象的__wakeup()成員函數(如果其存在)。
unserialize_callback_func指令:你可以設定在此過程中唄執行的回呼函數,如果某個未被定義的類應當在反序列化時被執行個體化(以避免獲得一個不完全的對象“__PHP_Incomplete_Class”)。你可以使用php.ini,ini_set()或者.htaccess來定義“unserialize_callback_func”。當一個未被定義的類被執行個體化時,它會被調用。屏蔽這個特性只需將其設為空白即可。
傳回值
返迴轉換後的數值,可能是布爾變數、實數、浮點數、字串、數組或者對象。
假如傳入的字串不可以被反序列化,則返回FALSE,同時拋出NOTICE錯誤。
(以上譯自PHP手冊)
PHP序列化執行個體
數組的序列化和反序列化
OK,讓我們來用執行個體學習一下,首先,請建立sample1.php檔案,我們在這個檔案中用如下語句來建立一個雜湊數組:
複製內容到剪貼簿
代碼:
<?php
$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age" => "20"
);
?>
為了測試這個數組的值,你可以使用print_r()函數來輸出數組,輸出的結果如下:
複製內容到剪貼簿
代碼:
Array
(
[type] => human
[name] => hanguofeng
[age] => 20
)
那麼我們將他來序列化一下,序列化的代碼如下:
複製內容到剪貼簿
代碼:
<?php
$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age" => "20"
);
$animal_ser=serialize($animal);
echo($animal_ser);
?>
這裡我們將數組$animal序列化,將返回的序列化字串儲存在變數$animal_ser中,並輸出,輸出的結果是:
複製內容到剪貼簿
代碼:
a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}
我們來簡單對這個字串進行一個解析:
a:3表示這是一個數組型的對象(a),他共有三個內建的對象(3)
大括弧裡面的部分是以逗號分割的對象運算式列表,以s:4:"type"為例,他表示一個字串(s),長度為4位(4),值為“type”,即雜湊數組的第一個元素的鍵。
後面的部分以此類推,我們不再贅述,你可以試試自己將各種對象序列化,看看序列化後的字串是如何構建的。
下面來看數組的反序列化,即將我們上面產生的序列化字串恢複為數組,代碼如下:
複製內容到剪貼簿
代碼:
<?php
$animal_ser='a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}';
$animal = unserialize($animal_ser);
print_r($animal);
?>
在第一行中,我們假設$animal_ser的值為上面獲得的序列化字串,在第二行將該字串恢複為開始的數組,並賦值給$animal,最後輸出$animal這個數組,此時的輸出和本節開始時輸出的原始數組是一樣的,即:
複製內容到剪貼簿
代碼:
Array
(
[type] => human
[name] => hanguofeng
[age] => 20
)
這樣我們就完成了數組的反序列化。
拓展知識—自訂對象的序列化和反序列化
對數組進行序列化是一個基礎操作,然而在實際的程式設計中,我們可能經常對其他類型的變數進行序列化,例如對某個自訂對象進行序列化,這裡有一個我們自己編寫的類A(儲存在classa.inc中):
複製內容到剪貼簿
代碼:
<?php
class A {
var $one = 1;
function show_one() {
echo $this->one;
}
}
?>
我們在如下代碼中建立類的執行個體並對該執行個體進行序列化:
複製內容到剪貼簿
代碼:
<?php
include("classa.inc");
$a=new A;
echo(serialize($a));
?>
此時輸出的內容為:
複製內容到剪貼簿
代碼:
O:1:"A":1:{s:3:"one";i:1;}
總體來看,這個序列化字串輸出了改對象當前的狀態,即i的值為1。下面我們來逐個分析其中的細節。
O:1:由於當前的變數是一個自訂對象,因此該表徵字元為“O”,表示Object。
後面的"A"標識了該變數是哪個類的執行個體,這裡即A類。
大括弧內即該執行個體的各個屬性的名稱和值。
而後我們將其進行反序列化:
複製內容到剪貼簿
代碼:
<?php
include("classa.inc");
$s = 'O:1:"A":1:{s:3:"one";i:1;}';
$a = unserialize($s);
$a->show_one();
?>
此時輸出“1”,即調用了A類的show_one()方法。
你可以注意到雖然在執行個體的序列化字串中並沒有包含類的方法,但是我們將其反序列化後,仍然可以調用類的方法,這個特性在PHP4及以上版本中被支援(當然,你需要包含類的定義檔案classa.inc)。
註:你可以參考PHP手冊中Language Reference->Classes and Objects->Serializing objects - objects in sessions一節的內容。
在JavaScript中序列化為JSON—使用json2.js
JavaScript中沒有直接序列化對象的內建方法,當然你可以自己寫一個,不過我還是強烈推薦你在這裡偷個小懶,使用現成的組件,JSON的官方網站www.json.org提供了對JavaScript對象實現JSON序列化的程式碼程式庫—json2.js,你可以從這裡獲得它。
獲得完畢json2.js檔案後,你可以開啟這個檔案,在檔案的前部分包含了相當大量的注釋資訊,如果你的英文足夠好,那麼你可以省略我這一節,參考該檔案的注釋就可以了,如果作為程式員,你已經看夠了大片的字母,想看看我的漢字+字母,那你可以向下繼續了。
簡單的翻譯下這個注釋:
可參考http://www.JSON.org/js.html
該檔案建立了一個包含兩個方法的全域對象JSON,它的方法分別是:
複製內容到剪貼簿
代碼:
JSON.stringify(value, whitelist)
value 任意的JavaScript值,一般是一個對象或者數組
whitelist 一個可選的數組參數,用於判定對象值如何被序列化
這個方法通過一個JavaScript值來產生JSON文本。在進行序列化時,根據可選的參數whitelist,有三種可能:
如果某個對象有toJSON方法,那麼則調用該方法,toJSON方法的傳回值將被序列化。
否則,如果選擇性參數whitelist是一個數組,那麼數組中的元素將被用來選擇對象進行序列化時的的成員。
否則,如果沒有使用whitelist參數,則對象的所有成員將被序列化。
如果值沒有JSON的表現形式,例如undefined或者函數,則其不會被序列化。在對象中,這樣的值會被忽略,而在數組中將會被null替換。
JSON.stringify(undefined)會返回undefined。日期將會被序列化為被引用的ISO日期。
例:
複製內容到剪貼簿
代碼:
var text = JSON.stringify(['e', {pluribus: 'unum'}]);
//text is '["e",{"pluribus":"unum"}]'
JSON.parse(text, filter)
該方法解析一個JSON文本,並產生一個組件或者數組,其可能拋出一個SyntaxError異常。
可選的filter參數是一個可過濾和轉換結果的函數、它接受每個鍵和值,它的傳回值用來替換源值。如果它返回所接收的值,那麼結果不會被改變。如果他返回undefined,則該成員會被刪除。
例:
複製內容到剪貼簿
代碼:
//解析文本,如果某個鍵包含字串“date”,則將其值轉換為日期
myData = JSON.parse(text, function (key, value) {
return key.indexOf('date') >= 0 ? new Date(value) : value;
});
上面的入門教程已經使你基本瞭解了json2.js的使用方法,這裡關於該檔案我就不再贅述了,只是有一個小提示,如果你想簡單的解析一個JSON文本,那麼可以使用eval()函數,改函數是JavaScript的內建函數,例如解析在JSON.stringify的案例中產生的JSON文本,可以使用:
複製內容到剪貼簿
代碼:
var myE = eval('["e",{"pluribus":"unum"}]');
來獲得對象myE。
在JavaScript中序列化為JSON—使用prototype.js
如果你和我一樣,喜歡在自己的項目中使用開源的JavaScript架構,那麼你可能可以省去使用json2.js檔案了,這裡以protype.js為例,該檔案可以在http://www.prototypejs.org下載,由於本文不是在講JavaScript架構,這裡我假設你對prototype.js的使用已經有所瞭解了。
prototype.js中提供了對Object對象的toJSON方法,你可以使用Object.toJSON()方法來實現對對象的序列化,例如:
複製內容到剪貼簿
代碼:
var cat=
{
name:"hellokitty",
height:"6 apples"
}
alert(Object.toJSON(cat));
//將彈出對話方塊,內容為 {"name": "hellokitty", "height": "6 apples"}
另外,在prototype.js中還有另外的JSON支援,主要是在Ajax對象中對Ajax返回請求中JSON內容的解析。這裡暫時與我們的內容無關,也不再介紹了。
PHP與JSON
在上面我們一起瞭解了PHP進行對象序列化的方法以及在JavaScript中進行將對象序列化為JSON的方法,你大致會質疑我為什麼將二者放在一起,因為他們的文法實際是不完全一樣的,然而,在PHP中,可以對JSON文本進行反序列化,也可以將PHP的對象序列化為JSON而非PHP風格的文本。這主要是靠json_decode和json_encode兩個函數來完成的,需要特別說明的是,這兩個函數在PHP 5 >= 5.2.0中才被支援,如果你要編寫運行在PHP4環境下的程式,那麼這兩個函數是不可以使用的。
json_decode函數
文法
mixed json_decode ( string $json [, bool $assoc] )
擷取一個JSON編碼文本,並且將其轉換為PHP變數
參數
json
被JSON編碼的文本
assoc
當為TRUE時,返回的值為聯合數組
傳回值
返回一個對象,或者如果可選的assoc參數為TRUE,則一個聯合數組將會被返回
json_encode函數
文法
string json_encode ( mixed $value )
該函數返回一個值的JSON運算式
參數
value
要被編碼的值,可以為除resource外的任何型別參數
這個函數僅在UTF-8編碼格式時起作用
傳回值
當成功時返回編碼後的JSON文本
json_decode函數執行個體
下面兩個例子都基於我們的一個情景假設,即,我們有一個使用者註冊的模組,這個模組以“物件導向”的方式工作,在json_decode函數執行個體中,我們在前台將使用者的註冊資訊變為一個類的屬性,而後傳遞到背景php檔案(這裡為了簡便,就不用Ajax了)。在json_encode執行個體中,我們在html檔案中引用一個js檔案,地址指向php檔案,在php檔案中輸出json編碼後的使用者物件(同樣為了簡便,我們直接產生一個對象而不從資料庫中取資訊),並在html中輸出。
好了,先來看前台的頁面json_encode.htm,這個頁面模仿了通常的註冊頁面,在其上面有一個表單,當提交時,觸發JavaScript函數,產生一個使用者物件user,將表單內容設為使用者物件的屬性,產生JSON文本,以POST方式傳遞到背景json_encode.php檔案。在js_encode.php檔案中,將JSON文本用json_decode函數解析為PHP對象,並輸出。
好了,先來看json_encode.html檔案,檔案代碼如下:
複製內容到剪貼簿
代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>json_decode</title>
<script src="json2.js" type="text/javascript"></script>
<script type="text/javascript">
function JSON_test(o)
{
var user = {
name:document.getElementById('txt_name').value,
email:document.getElementById('txt_email').value,
password:document.getElementById('txt_name').value
}
var json_string = JSON.stringify(user);
document.getElementById('txt_json').value=json_string;
alert("點擊確定後將提交表單");
o.submit();
}
</script>
</head>
<body>
<form id="form1" name="form1" method="post" action="json_encode.php" onsubmit="JSON_test(this)">
<label for="txt_name">姓名</label>
<p>
<input type="text" name="txt_name" id="txt_name" />
</p>
<label for="txt_email">郵箱</label>
<p>
<input type="text" name="txt_email" id="txt_email" />
</p>
<p>
<label for="txt_password">密碼</label>
</p>
<p>
<input type="text" name="txt_password" id="txt_password" />
</p>
<p>
<input type="text" name="txt_json" id="txt_json" />
<label for="button"></label>
<input type="submit" name="button" id="button" value="JSON" />
</p>
</form>
</body>
</html>
當提交表單時,將觸發JavaScript函數JSON_text(),該函數首先建立一個JavaScript對象user,將其name、email和password屬性分別設為對應表單的值,而後使用json2.js檔案的JSON.stringify方法將其轉換為JSON文本json_string,最後設定隱藏欄位(這裡為了使你看的清楚,我把這個隱藏欄位以文字框形式顯示了)txt_json的值為json_string,並提交表單。
下面到json_encode.php檔案,如下:
複製內容到剪貼簿
代碼:
<?php
$json_string = $_POST["txt_json"];
if(ini_get("magic_quotes_gpc")=="1")
{
$json_string=stripslashes($json_string);
}
$user = json_decode($json_string);
echo var_dump($user);
?>
在這個檔案中,首先得到json_encode.html檔案中POST表單域txt_json的值,放入變數$json_string中,而後判斷,如果當前PHP的設定為magic_quotes_gpc=On,即傳入的雙引號等會被轉義,這樣json_decode函數無法解析,因此我們要將其反轉義化。而後,使用json_decode函數將JSON文本轉換為對象,儲存在$user變數中,最終用echo var_dump($user);,將該對象dump輸出出來,最終結果是:
複製內容到剪貼簿
代碼:
object(stdClass)#1 (3) {
["name"]=>
string(10) "hanguofeng"
["email"]=>
string(18) "example@domain.com"
["password"]=>
string(10) "hanguofeng"
}
json_encode函數執行個體
在這個例子中,仍然是由兩部分構成的,即json_enode.html和json_encode.php。在json_decode.html檔案中,基本與json_decode.html檔案的表單類似,但是不同的是,這次我們要從json_encode.php檔案中獲得相應使用者的JSON文本,先來看這個PHP檔案吧:
複製內容到剪貼簿
代碼:
<?php
Class user
{
public $name="";
public $email="";
public $password="";
};
$myUser = new user;
$myUser->name="hanguofeng";
$myUser->email="example@domain.com";
$myUser->password="hanguofeng";
$json_string = json_encode($myUser);
?>
var user = <?php echo($json_string)?>;
這個檔案首先建立類user,而後獲得一個user類的執行個體myUser,並設定其使用者名稱、郵箱和密碼,接下來使用json_encode函數將其轉換為JSON文本,儲存在變數$json_string中,最後輸出一段JavaScript代碼,以在JavaScript中建立全域變數user。
接下,我們需要在json_encode.html檔案中引入json_encode.php檔案,並得到user對象的各個屬性,如下:
複製內容到剪貼簿
代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>json_encode</title>
<script src="json_encode.php" type="text/javascript"></script>
</head>
<body>
<form id="form1" name="form1" method="post">
<label for="txt_name">姓名</label>
<p>
<input type="text" name="txt_name" id="txt_name" />
</p>
<label for="txt_email">郵箱</label>
<p>
<input type="text" name="txt_email" id="txt_email" />
</p>
<p>
<label for="txt_password">密碼</label>
</p>
<p>
<input type="text" name="txt_password" id="txt_password" />
</p>
</form>
<script type="text/javascript" >
document.getElementById('txt_name').value=user.name;
document.getElementById('txt_email').value=user.email;
document.getElementById('txt_password').value=user.password;
</script>
</body>
</html>
在這個檔案中,你需要注意兩點,第一是,我們以這樣的代碼引入json_encode.php檔案為JavaScript檔案:
複製內容到剪貼簿
代碼:
<script src="json_encode.php" type="text/javascript"></script>
第二點則是:
我們在文字框代碼後面加入JavaScript的代碼,對文字框的value屬性進行操作,分別設定其值為user對象的相應值。
實踐出真知
背景說明
前台JavaScript部分
後台PHP部分