Golang中传递空接口导致类型变化的问题
Golang中传递空接口导致类型变化的问题 大家好
我正在编写一些代码来测试JSON反序列化。为了避免编写大量代码,我写了一个函数,你可以传入JSON、一个将作为指针传递给Go的Unmarshal方法的容器,以及我期望的结构体。
这样就不需要编写大量使用以下语法的测试:
有效的代码:
var holderNull1 int32Holder
jsonNull1 := "{}"
if err := json.Unmarshal([]byte(jsonNull1), &holderNull1); err != nil {
t.Error("Unexpected error ", err)
}
assertEq(holderNull1,int32Holder{Holder: NewInt32OptEmpty()}, t)
我写了这个辅助函数:
func testUnmarshal(jsonValue string, holder interface{}, expect interface{}, t *testing.T) {
if err := json.Unmarshal([]byte(jsonValue), &holder); err != nil {
t.Error("Unexpected error ", err)
}
println(holder)
assertEq(holder,expect, t)
}
并这样调用它:
testUnmarshal("{}", int32Holder{}, int32Holder{Holder: NewInt32OptEmpty()}, t)
第二种方法显示,传入的容器类型是 map[string]interface {},所以将一个结构体传递给函数,然后将其作为指针传递似乎改变了它的类型。
我漏掉了什么?
我在Go Playground上整理了一个小例子:
更多关于Golang中传递空接口导致类型变化的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
int2Holder 的类型定义是什么?代码是否可以在某处获取,以便我们尝试运行并复现问题?如果不行,您能否提供一个最小可复现的场景?
更多关于Golang中传递空接口导致类型变化的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我在 Go Playground 上整理了一个小例子:
通过传递地址更新了代码。以下是可运行的版本:
问题在于你的辅助函数中 holder 参数的类型声明和 json.Unmarshal 的调用方式。当你将 int32Holder{} 作为 interface{} 参数传递时,函数内部接收到的是该结构体的值拷贝。随后你对其取地址 &holder,实际上获取的是这个接口变量本身的地址,而非原始结构体的地址。json.Unmarshal 接收到一个指向 interface{} 的指针,它会在内部创建一个新的 map[string]interface{} 来解析 JSON,而不是填充你期望的结构体。
以下是修正后的代码示例:
func testUnmarshal(jsonValue string, holder interface{}, expect interface{}, t *testing.T) {
// 通过反射获取 holder 的指针,确保 Unmarshal 能修改原始值
holderPtr := reflect.ValueOf(holder).Addr().Interface()
if err := json.Unmarshal([]byte(jsonValue), holderPtr); err != nil {
t.Error("Unexpected error ", err)
}
// 比较时使用解引用后的值
assertEq(reflect.ValueOf(holderPtr).Elem().Interface(), expect, t)
}
调用方式需要调整为传递结构体的指针:
var holder int32Holder
testUnmarshal("{}", &holder, int32Holder{Holder: NewInt32OptEmpty()}, t)
或者,更简洁的实现是直接要求调用者传递指针:
func testUnmarshal(jsonValue string, holderPtr interface{}, expect interface{}, t *testing.T) {
if err := json.Unmarshal([]byte(jsonValue), holderPtr); err != nil {
t.Error("Unexpected error ", err)
}
assertEq(holderPtr, expect, t)
}
调用方式:
var holder int32Holder
testUnmarshal("{}", &holder, int32Holder{Holder: NewInt32OptEmpty()}, t)
关键点:
json.Unmarshal必须接收一个指向目标类型的指针- 当通过
interface{}传递参数时,需要确保传递的是指针的接口,而不是值的接口 - 使用反射可以处理通用情况,但直接要求指针参数更清晰高效
你的 Playground 示例修正后:
func testUnmarshal(jsonValue string, holder interface{}, expect interface{}, t *testing.T) {
holderPtr := reflect.ValueOf(holder).Addr().Interface()
if err := json.Unmarshal([]byte(jsonValue), holderPtr); err != nil {
t.Error("Unexpected error ", err)
}
actual := reflect.ValueOf(holderPtr).Elem().Interface()
if !reflect.DeepEqual(actual, expect) {
t.Errorf("Expected %v, got %v", expect, actual)
}
}

