深入學習PHP中的JSON相關(guān)函數(shù)在我們當年剛剛上班的那個年代,還全是 XML 的天下,但現(xiàn)在 JSON 數(shù)據(jù)格式已經(jīng)是各種應(yīng)用傳輸?shù)氖聦崢藴柿?。最近幾年開始學習編程開發(fā)的同學可能都完全沒有接觸過使用 XML 來進行數(shù)據(jù)傳輸。當然,時代是一直在進步的,JSON 相比 XML 來說,更加地方便快捷,可讀性更高。但其實從語義的角度來說,XML 的表現(xiàn)形式更強。 話不多說,在 PHP 中操作 JSON 其實非常簡單,大家最常用的無非也就是 json_encode() 和 json_decode() 這兩個函數(shù)。它們有一些需要注意的地方,也有一些好玩的地方。今天,我們就來深入地再學習一下。 JSON 編碼首先,我們準備一個數(shù)組,用于我們后面編碼的操作。 $data = [ 非常簡單地數(shù)組,其實也沒有什么特別的東西,只是有數(shù)據(jù)的嵌套,有一些中文和特殊符號而已。對于普通的 JSON 編碼來說,直接使用 json_encode() 就可以了。 $json1 = json_encode($data); 中文處理上面編碼后的 JSON 數(shù)據(jù)發(fā)現(xiàn)了什么問題沒?沒錯,相信不少人一眼就會看出,中文字符全被轉(zhuǎn)換成了 \uxxxx 這種格式。這其實是在默認情況下,json_encode() 函數(shù)都會將這些多字節(jié)字符轉(zhuǎn)換成 Unicode 格式的內(nèi)容。我們直接在 json_encode() 后面增加一個常量參數(shù)就可以解決這個問題,讓中文字符正常地顯示出來。 $json1 = json_encode($data, JSON_UNESCAPED_UNICODE); 當然,只是這樣就太沒意思了。因為我曾經(jīng)在面試的時候就有一位面試官問過我,如果解決這種問題,而且不用這個常量參數(shù)。大家可以先不看下面的代碼,思考一下自己有什么解決方案嗎? function t($data) 其實就是一個很簡單地解決方案,遞歸地將數(shù)據(jù)中所有字段內(nèi)容轉(zhuǎn)換成 urlencode() 編碼,然后再使用 json_encode() 編碼,完成之后再使用 urldecode() 反解出來。是不是有點意思?其實這是不少老程序員的一個小技巧,因為 JSON_UNESCAPED_UNICODE 這個常量是在 PHP5.4 之后才有的,之前的話如果想讓編碼后的數(shù)據(jù)直接顯示中文,就只能這樣操作了。 當然,現(xiàn)在已經(jīng)是 PHP8 時代了,早就已經(jīng)不需要這么麻煩地操作了,不過也不能排除有些面試館仗著自己是老碼農(nóng)故意出些這樣的題目。大家了解下,知道有這么回事就可以了,畢竟在實際的項目開發(fā)中,使用 PHP5.4 以下版本的系統(tǒng)可能還真是非常少了(這樣的公司不去也罷,技術(shù)更新得太慢了)。 其它參數(shù)除了 JSON_UNESCAPED_UNICODE 之外,我們還有許多的常量參數(shù)可以使用,而且這個參數(shù)是可以并行操作的,也就是可以多個常量參數(shù)共同生效。 $json1 = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_NUMERIC_CHECK | JSON_HEX_QUOT); 這一堆參數(shù)其實是針對的我們數(shù)據(jù)中的一些特殊符號,比如說 & 符、<> HTML 標簽等。當然,還有一些常量參數(shù)沒有全部展示出來,大家可以自己查閱官方手冊中的說明。 另外,json_encode() 還有第三個參數(shù),代表的是迭代的層級。比如我們上面的這個數(shù)據(jù)是多維數(shù)組,它有三層,所以我們至少要給到 3 才能正常地解析。下面代碼我們只是給了一個 1 ,所以返回的內(nèi)容就是 false 。也就是無法編碼成功。默認情況下,這個參數(shù)的值是 512 。 var_dump(json_encode($data, JSON_UNESCAPED_UNICODE, 1)); // bool(false) 對象及格式處理默認情況下,json_encode() 會根據(jù)數(shù)據(jù)的類型進行編碼,所以如果是數(shù)組的話,那么它編碼之后的內(nèi)容就是 JSON 的數(shù)組格式,這時我們也可以添加一個 JSON_FORCE_OBJECT ,讓它將一個數(shù)組以對象的形式進行編碼。 $data = []; 之前在講數(shù)學相關(guān)函數(shù)的時候我們學習過,如果數(shù)據(jù)中有 NAN 這種數(shù)據(jù)的話,json_encode() 是無法編碼的,其實我們可以添加一個 JSON_PARTIAL_OUTPUT_ON_ERROR ,對一些不可編碼的值進行替換。下面的代碼中,我們就可以使用它讓 NAN 替換成 0 。 $data = NAN; 對象編碼的屬性問題對于對象來說,JSON 編碼后的內(nèi)容就和序列化一樣,只會有對象的屬性而不會有方法。畢竟 JSON 最大的用處就是用于數(shù)據(jù)傳輸?shù)?,方法對于?shù)據(jù)傳輸來說沒有什么實際的作用。而屬性也會根據(jù)它的封裝情況有所不同,只會編碼公共的,也就是 public 的屬性。 $data = new class 從這段測試代碼中可以看出,protected 、 private 屬性以及那個方法都不會被編碼。 JSON 解碼對于 JSON 解碼來說,其實更簡單一些,因為 json_decode() 的常量參數(shù)沒有那么多。 var_dump(json_decode($json1)); 首先還是看下它的第二個參數(shù)。這個參數(shù)的作用其實從代碼中就可以看出來,如果不填這個參數(shù),也就是默認情況下它的值是 false ,那么解碼出來的數(shù)據(jù)是對象格式的。而我們將這具參數(shù)設(shè)置為 true 的話,那么解碼后的結(jié)果就會是數(shù)組格式的。這個也是大家非常常用的功能,就不多做解釋了。 var_dump(json_decode('{"a":1321231231231231231231231231231231231231231231231231231231231231231231233}', true)); 對于這種非常長的數(shù)字格式的數(shù)據(jù)來說,如果直接 json_decode() 解碼的話,它會直接轉(zhuǎn)換成 科學計數(shù)法 。我們可以直接使用一個 JSON_BIGINT_AS_STRING 常量參數(shù),將這種數(shù)據(jù)在解碼的時候直接轉(zhuǎn)換成字符串,其實也就是保留了數(shù)據(jù)的原始樣貌。注意,這里 json_decode() 函數(shù)的參數(shù)因為有那個轉(zhuǎn)換對象為數(shù)組的參數(shù)存在,所以它有四個參數(shù),第三個參數(shù)是迭代深度,第四個就是定義這些格式化常量值的。而且它和 json_encode() 是反過來的,迭代深度參數(shù)在前,格式常量參數(shù)在后面,這里一定要注意哦! 如果數(shù)據(jù)是錯誤的,那么 json_decode() 會返回 NULL 。 var_dump(json_decode("", true)); // NULL 錯誤處理上面兩段代碼中我們都演示了如果編碼或解碼的數(shù)據(jù)有問題會出現(xiàn)什么情況,比如 json_encode() 會返回 false ,json_decode() 會返回 NULL 。但是具體的原因呢? $data = NAN; 沒錯,json_last_error() 和 json_last_error_msg() 就是返回 JSON 操作時的錯誤信息的。也就是說,json_encode() 和 json_decode() 在正常情況下是不會報錯的,我們?nèi)绻@得錯誤信息,就得使用這兩個函數(shù)來獲取。這一點也是不少新手小同學沒有注意過的地方,沒錯誤信息,不拋出異常問題對我們的開發(fā)調(diào)試其實是非常不友好的。因為很可能找了半天都不知道問題出在哪里。 在 PHP7.3 之后,新增加了一個常量參數(shù),可以讓我們的 json_encode() 和 json_decode() 在編解碼錯誤的時候拋出異常,這樣我們就可以快速地定位問題了,現(xiàn)在如果大家的系統(tǒng)運行環(huán)境是 PHP7.3 以上的話,非常推薦使用這個常量參數(shù)讓系統(tǒng)來拋出異常。 // php7.3 JSON_THROW_ON_ERROR 是對 json_encode() 和 json_decode() 都起效的。同樣,只要設(shè)定了這個常量參數(shù),我們就可以使用 try...catch 來進行捕獲了。 try { JSON 序列化接口在之前的文章中,我們學習過 使用Serializable接口來自定義PHP中類的序列化 。也就是說,通過 Serializable 接口我們可以自定義序列化的格式內(nèi)容。而對于 JSON 來說,同樣也提供了一個 JsonSerializable 接口來實現(xiàn)我自定義 JSON 編碼時的對象格式內(nèi)容。 class jsontest implements JsonSerializable 這是一個小的示例,只需要實現(xiàn) JsonSerializable 接口中的 jsonSerialize() 方法并返回內(nèi)容就可以實現(xiàn)這個 jsontest 對象的 JSON 編碼格式的指定。這里我們只是簡單地返回了數(shù)據(jù)的內(nèi)容,其實和普通的 json_encode() 沒什么太大的區(qū)別。下面我們通過一個復雜的例子看一下。 class Student implements JsonSerializable 在這個例子中,我們在 jsonSerialize() 做了一些操作。如果數(shù)據(jù)沒有傳值,比如為 null 的情況下就給一個默認值。然后在 id 為 2 的情況下返回一個普通數(shù)組。大家可以看到最后一段注釋中的第二條數(shù)據(jù)的格式。 這個接口是不是很有意思,相信大家可能對上面的 json_encode() 和 json_decode() 非常熟悉了,但這個接口估計不少人真的是沒接觸過,是不是非常有意思。 總結(jié)果然,什么事情都怕深挖。不學不知道,一學嚇一跳,平常天天用得這么簡單的 JSON 操作的相關(guān)函數(shù)其實還有很多好用的功能是我們不知道的。當然,最主要的還是看看文檔,弄明白并且記住一些非常好用的常量參數(shù),另外,拋出異常的功能也是這篇文章的重點內(nèi)容,建議版本達到的朋友最好都能使用 JSON_THROW_ON_ERROR 來讓錯誤及時拋出,及時發(fā)現(xiàn)哦! 測試代碼: https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/11.深入學習PHP中的JSON相關(guān)函數(shù).php 參考文檔: https://www./manual/zh/book.json.php |
|
來自: 硬核項目經(jīng)理 > 《待分類》