json: cannot unmarshal string into Go value of type xxx

今天在工作时,遇到了一个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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值