Learning Goang Type Assertions

Posted by Kathy on Mon, 03 Jun 2019 23:24:04 +0200

There is a serialize() function in php that serializes arrays into strings for storage and transmission.

If you want to deserialize this string, you only need a simple unserialize() function in php. But it's not so easy in golang. It takes a lot of effort to write a lot of code.

At this time, I just want to sigh that php is really the best language in the world.

I have encountered such a problem in today's development that I need to use golang to parse php serialized strings, find a parsing package on github, but find that the result of parsing is an interface {} type.

Suddenly, I couldn't get started. I always thought that the data was actually a map, but after parsing, I got an interface {}, which let me use it.

Feeling that you need to use type assertion, but not very good, so go to the community to ask, hoping that the big guys can give an answer.

It really works.

Next, paste the code:

package main

import (
	"github.com/yvasiyarov/php_session_decoder/php_serialize"
	"fmt"
	"log"
)


func main() {
        // Serialized strings
	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";}}}`
	
        // Deserialize
        decoder := php_serialize.NewUnSerializer(str)
	if result, err := decoder.Decode(); err != nil {
		panic(err)
	} else {
                // Type assertions are made here
		decodeData, ok := result.(php_serialize.PhpArray)
		if !ok {
			log.Println(err)
		}

                // After assertion, you can get the content
		name := decodeData["name"]
		age := decodeData["age"]
		fmt.Println(name, age)

                // The underlying data still needs to be asserted again
		friends, ok := decodeData["friends"].(php_serialize.PhpArray)
		if !ok {
			log.Println(err)
		}

                // Once the assertion is successful, internal data can be obtained
		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)
		}
	}
}

 

It can be roughly understood that the type of a variable is what it is, the type of assertion is made, and the result is obtained after the assertion.

How to determine the type of a variable?

Print it out: fmt.Printf("%T", verb)

% T This placeholder shows the type of variable

 

Here is another example:

package main

import (
	"github.com/yvasiyarov/php_session_decoder/php_serialize"
	"fmt"
	"log"
)

func main() {
	// Serialized strings
	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";}}}`
	
	// Deserialize
	decoder := php_serialize.NewUnSerializer(str)
	result, err := decoder.Decode()
	if err != nil {
		panic(err)
	}
	// Type Asserts
	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])
		}
	}
}

The above two demo s can achieve results, but they are written differently.

Later, I was introduced and got another bag, which can also achieve the effect:

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"])
	}
}

The type of all the results parsed by this package is map[interface{}]interface {}, so it's easier to be rude when making type assertions.

Below are my questions on segmentfault and stack overflow. The demo example above is also from the answers of the big guys in the questions. Interested to see:

segmentfault:https://segmentfault.com/q/1010000010690732

stackoverflow:https://stackoverflow.com/questions/45705930/how-to-get-values-from-a-interface-in-golang/

Topics: Go PHP github