今天在工作时,遇到了一个json序列化问题。以下为代码:
type testStruct struct {
ID int `json:"ID"`
Score float64 `json:"Score"`
}
func testJSON(tStruct interface{}) []testStruct {
structs := make([]testStruct, 0)
jsonScore, _ := json.Marshal(tStruct)
if err := json.Unmarshal(jsonScore, &structs); err != nil {
fmt.Println(err)
return structs
}
return structs
}
func main() {
structs := []testStruct{
{
ID: 1,
Score: 1.0,
},
{
ID: 1,
Score: 2.0,
},
}
jsonValue, _ := json.Marshal(structs)
tStructs := testJSON(jsonValue)
fmt.Println(tStructs)
}
这里在testJSON序列化json时,先将参数序列化为jsonScore,再将其反序列化回原始值。这样可能想的是忽略接口原始传入的值类型,正常序列化。
但是debug时发现,进入该函数时已经将原始值当做了 []uint8 类型。
如果此时再对[]byte进行Marshal,会产生乱码。
用这个乱码肯定是反序列化不回去的,所以会报了错:json: cannot unmarshal string into Go value of type
直接将函数代码改为:
func testJSON(tStruct interface{}) []testStruct {
structs := make([]testStruct, 0)
bytes := tStruct.([]byte)
if err := json.Unmarshal(bytes, &structs); err != nil {
fmt.Println(err)
return structs
}
return structs
}
此时成功序列化了。
这种方法应该是想着传入的类型是原始类型的数据,这样先序列化再反序列化就能忽略原始类型的影响。但是传入的是已经序列化的[]byte就不可以了。问题还是主要出在如果对[]byte类型的数据进行序列化,会生成字符类型的乱码。
func main(){
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
fmt.Println(string(jsonData)) //
}
打印值:“SGVsbG8sIHdvcmxkIQ==”
25/1/24后补:上面的例子可能有问题,但是整体就是这个意思,具体形容下场景以备后患:
接口传入时,http 参数用结构体接收,后传入如testJSON的函数,由于空接口内包含的是结构体切片,所以在函数内 Marshal 后再 UnMarshal就可以正常返回去;而有的传参是来源于gorm 数据库 interface{}类型的字段,所以从数据库读取进程序,空接口内是[]byte 的类型。直接使用 []byte 类型进行 JSON 序列化时,Go 的 JSON 库会将其视为一个字符串进行处理。因为 JSON 本身没有字节数组的概念,[]byte 会被编码为一个字符串,然后再 Marshal 后就会得到一个对字符串 Marshal 的结果,也就是嵌套序列化字符串,此时自然无法打回到原数组。
另外,如果mysql字段类型为 json,可以直接使用 gorm库自带的 datatypes.JSON类型来命名,虽然这个类型也是对[]byte 的封装,可是其内重载了 Marshal 方法:
type RawMessage []byte
type JSON json.RawMessage
// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return m, nil
}
func (j JSON) MarshalJSON() ([]byte, error) {
return json.RawMessage(j).MarshalJSON()
}
可以看到,如果用了这个类型,虽然从数据库内拿到的是[]byte类型(封装为datatypes.JSON),但是在Marshal 时并没有对其进行字符串二次序列,而是直接返回了原始的[]byte,所以并不会出现二次序列化的问题,将这个类型的字段传入如testJSON