This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

1, the preface

A problem encountered once when deserializing a JSON string to interface{}. Let’s start with this code:

import (
   "encoding/json"
   "fmt"
   "testing"
)

type Demo struct {
   Errno int32 `json:"errno"`
   ErrMsg string `json:"errmsg"`
   Data []string `json:"data"`
}

type Result struct {
   Errno int32 `json:"errno"`
   ErrMsg string `json:"errmsg"`
   Data interface{} `json:"data"`
}
 
func TestA(t *testing.T)  {
   demo := Demo{
      Errno: 0,
      ErrMsg: "success",
      Data: []string{"hello"."world"}},Serialize the Demo structure
   s,_ := json.Marshal(demo)
   var res Result
   // deserialize
   _ = json.Unmarshal(s, &res)
   data, ok := res.Data.([]string)
   fmt.Println("data:", data)   / / []
   fmt.Println("ok:", ok)       // false
}
Copy the code

When deserializing, the Data field Data is received with type interface{}. The conversion failed when the original []string was converted. Print the deserialized Data field type: []interface{}.

fmt.Printf("%T\n", res.Data) // []interface {}
Copy the code

2, analysis,

Two questions can be drawn from the printed results:

  1. why[]interface{}Type conversion to[]stringType will fail?
  2. Why did it turn out[]stringJson serialized and deserialized, and the type becomes[]interface{}?

Let’s do a small test in the IDE with the following code 👇 :

var dataSlice = []string{
   "a"."b",}var interfaceSlice []interface{} = dataSlice
Copy the code

Cannot use ‘dataSlice’ (type []string) as the type []interface{}. Interface {} can be assigned to any type, but 👆case found that it is not possible to assign any type of slice to []interface{}.

3, why

3.1. Type conversion fails

  1. []interface{}The type is notinterface{}Type, which is a slice, and the type of the slice element happens to beinterface{}.
  2. []interface{}Type variables have a specific memory structure, which is determined at compile time.

Each interface{} has two words, one for the type of the interface and the other for the actual data or a pointer to the data. Therefore, behind the []interface{} slice of type N length is an N*2 word long block of data.

This is different from the normal []MyType slice, where the sizeof the data block behind the []MyType slice of the same length is N*sizeof(MyType) word length.

It is for these two reasons that some slices of []MyType cannot be assigned directly to []interface{}, because they represent different data meanings.

Similarly, we cannot convert the []interface{} type to []string.

3.2 Deserialization failed after serialization

This problem exists if we use an interface{} type to receive json deserialized fields. If you want to deserialize a JSON string to an Interface field, the following collection types are returned:

bool.for JSON booleans
float64.for JSON numbers
string.for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
Copy the code

For all JSON array types, you get the []interface{} type.

4. Solutions

As a result of the above analysis, when we need to get a string in a deserialized slice, we can only loop through the []interface{} slice to convert each element to string. Otherwise, we need to explicitly use [] String to receive deserialized fields. For example:

var res Result
_ = json.Unmarshal(s, &res)
 
data1, ok := res.Data.([]interface{})
for _, item := range data1 {
   s, ok := item.(string)
   fmt.Println(s, ok) 
}
// log
//hello true
//world true
Copy the code

🔗 : 1, github.com/golang/go/w… 2, PKG. Go. Dev/encoding/js…