golang 類型斷言的學習


在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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM