Go中以太坊合约解码所得interface{}转自定义struct触发panic问题
问题原因与解决方案
根本原因
Go语言的结构体类型唯一性由字段顺序、字段类型、字段标签三个属性共同决定,哪怕两个结构体的字段名、字段类型完全一致,只要标签不同,就属于完全独立的类型,不支持直接类型断言。
你从合约解码拿到的是带json标签的匿名结构体,你自定义的OneInchSwapDataDesc没有对应标签,因此类型断言触发panic。
解决方案
方案1:对齐结构体标签(成本最低)
直接给自定义结构体加上和匿名结构体一致的json标签:
type OneInchSwapDataDesc struct { SrcToken common.Address `json:"srcToken"` DstToken common.Address `json:"dstToken"` SrcReceiver common.Address `json:"srcReceiver"` DstReceiver common.Address `json:"dstReceiver"` Amount *big.Int `json:"amount"` MinReturnAmount *big.Int `json:"minReturnAmount"` Flags *big.Int `json:"flags"` Permit []uint8 `json:"permit"` }
如果可以调整解码逻辑,直接将解码目标指定为*OneInchSwapDataDesc类型,解码完成后可直接使用,无需额外转换,这是最优解。
如果不能调整解码逻辑,修改标签后也可以直接做类型转换,只是需要完整声明匿名结构体类型:
anonStruct := data.(struct { SrcToken common.Address `json:"srcToken"` DstToken common.Address `json:"dstToken"` SrcReceiver common.Address `json:"srcReceiver"` DstReceiver common.Address `json:"dstReceiver"` Amount *big.Int `json:"amount"` MinReturnAmount *big.Int `json:"minReturnAmount"` Flags *big.Int `json:"flags"` Permit []uint8 `json:"permit"` }) res := OneInchSwapDataDesc(anonStruct)
方案2:JSON中转转换(无需改解码逻辑,代码简洁)
如果不想修改结构体定义,也可以用JSON做一次中转,普通业务场景下性能损耗可忽略:
import "encoding/json" // data为解码拿到的interface{}类型变量 var res OneInchSwapDataDesc jsonBytes, err := json.Marshal(data) if err != nil { // 处理序列化错误 } err = json.Unmarshal(jsonBytes, &res) if err != nil { // 处理反序列化错误 } // res即为转换完成的自定义结构体实例
方案3:反射拷贝字段(性能最优)
如果对性能要求很高,可以用反射逐个拷贝字段值,无需序列化开销:
import "reflect" var res OneInchSwapDataDesc srcVal := reflect.ValueOf(data) dstVal := reflect.ValueOf(&res).Elem() for i := 0; i < srcVal.NumField(); i++ { dstField := dstVal.FieldByName(srcVal.Type().Field(i).Name) if dstField.IsValid() && dstField.CanSet() { dstField.Set(srcVal.Field(i)) } } // res即为转换完成的自定义结构体实例
内容的提问来源于stack exchange,提问作者La Gregance
相关产品推荐
相关产品推荐

