在php中有一個 serialize() 函數 可以把數組序列化成字符串進行存儲和傳輸
如果想反序列化這種字符串,在php中只需要一個簡單的unserialize() 函數就可以完成了.但是在golang中可就沒有這么容易了,非得費個九牛二虎之力,寫上不少代碼才行。
這時候只想感嘆一下,php真的是世界上最好的語言啊!
我就在今天的開發中遇到了這么個問題,需要使用golang去解析php序列化的字符串,在github上找了個解析的包,但是發現解析之后的結果是個interface{}類型。
頓時我就無從下手了,總在想數據實際上是一個map,但是解析后得到一個interface{} , 這讓我怎么用啊
感覺需要用類型斷言,但又不太會用,遂去社區問一下吧,期望大佬們能給個答案。
實際上確實很管用。
下面貼一下代碼:
package main
import (
"github.com/yvasiyarov/php_session_decoder/php_serialize"
"fmt"
"log"
)
func main() {
// 序列化后的字符串
str := `a:3:{s:4:"name";s:3:"tom";s:3:"age";s:2:"23";s:7:"friends";a:2:{i:0;a:1:{s:4:"name";s:5:"jerry";}i:1;a:1:{s:4:"name";s:4:"jack";}}}`
// 反序列化
decoder := php_serialize.NewUnSerializer(str)
if result, err := decoder.Decode(); err != nil {
panic(err)
} else {
// 此處進行類型斷言
decodeData, ok := result.(php_serialize.PhpArray)
if !ok {
log.Println(err)
}
// 斷言后,即可獲取內容
name := decodeData["name"]
age := decodeData["age"]
fmt.Println(name, age)
// 內層數據仍需再次斷言
friends, ok := decodeData["friends"].(php_serialize.PhpArray)
if !ok {
log.Println(err)
}
// 斷言成功后即可獲取內部數據
for _,v := range friends {
fmt.Printf("type:%T, value:%+v\n", v,v )
friend, ok := v.(php_serialize.PhpArray)
if !ok {
log.Println(err)
}
friendName := friend["name"]
fmt.Println(friendName)
}
}
}
可以粗暴的這么理解:一個變量是什么類型,就進行什么類型的斷言,斷言后,即可得到結果
怎么判斷一個變量的類型?
打印出來呀:fmt.Printf("%T", verb)
%T這個占位符可以顯示變量的類型
下面還有個示例:
package main
import (
"github.com/yvasiyarov/php_session_decoder/php_serialize"
"fmt"
"log"
)
func main() {
// 序列化后的字符串
str := `a:3:{s:4:"name";s:3:"tom";s:3:"age";s:2:"23";s:7:"friends";a:2:{i:0;a:1:{s:4:"name";s:5:"jerry";}i:1;a:1:{s:4:"name";s:4:"jack";}}}`
// 反序列化
decoder := php_serialize.NewUnSerializer(str)
result, err := decoder.Decode()
if err != nil {
panic(err)
}
// 類型斷言
t := result.(php_serialize.PhpArray)
strVal := php_serialize.PhpValueString(t["name"])
fmt.Println(strVal)
switch t := result.(type) {
default:
fmt.Printf("unexpected type %T\n", t)
case php_serialize.PhpArray:
fmt.Println(t)
fmt.Println(t["name"])
fmt.Println(t["age"])
switch f := t["friends"].(type) {
default:
fmt.Printf("unexpected type %T\n", t)
case php_serialize.PhpArray:
fmt.Println(f)
fmt.Println(f[0])
fmt.Println(f[1])
}
}
}
上面兩個demo都可以達到效果,只是寫法不同。
后面我又經人介紹,得到了另外一個包,也能達到效果:
package main
import (
"fmt"
"log"
"github.com/wulijun/go-php-serialize/phpserialize"
)
func main() {
str := `a:3:{s:4:"name";s:3:"tom";s:3:"age";s:2:"23";s:7:"friends";a:2:{i:0;a:1:{s:4:"name";s:5:"jerry";}i:1;a:1:{s:4:"name";s:4:"jack";}}}`
decodedRes, err := phpserialize.Decode(str)
if err != nil {
panic(err)
}
//fmt.Printf("%T\n", decodedRes) //type is map[interface{}]interface{}
//type assert
decodedData, ok := decodedRes.(map[interface{}]interface{})
if !ok {
fmt.Printf("unexpected type %T\n", decodedRes)
}
fmt.Println(decodedData["name"])
fmt.Println(decodedData["age"])
//fmt.Printf("%T\n", decodedData["friends"]) // type is map[interface{}]interface{}
// type assert
friendsRes, ok := decodedData["friends"].(map[interface{}]interface{})
if !ok {
fmt.Printf("unexpected type %T\n", decodedData["friends"])
}
for _,v := range friendsRes {
//fmt.Printf("type: %T, val: %#v\n", v,v) // type is map[interface{}]interface{}
friend, ok := v.(map[interface{}]interface{})
if !ok {
fmt.Printf("unexpected type %T\n", decodedData["friends"])
}
//fmt.Printf("type: %T, val: %#v\n", friend,friend) // type is map[interface{}]interface{}
fmt.Println(friend["name"])
}
}
這個包解析出來的所有結果的類型都是map[interface{}]interface{},所以做類型斷言的時候可以簡單粗暴一些
下面是我在segmentfault和stackoverflow上面的提問,我上面的示例demo也是來源於問題中大佬們的回答,有興趣可以看下:
segmentfault:https://segmentfault.com/q/1010000010690732
stackoverflow:https://stackoverflow.com/questions/45705930/how-to-get-values-from-a-interface-in-golang/
