Golang中UnmarshalTypeError不再提供Field name的问题
Golang中UnmarshalTypeError不再提供Field name的问题
在我的代码中,我为特定类型使用了自定义解组器。当发生错误时,我的代码会返回一个 json.UnmarshalTypeError
这允许在类型与标准 JSON 解组器一起在结构中使用时,检索发生错误的字段名称
自从我今天迁移到 go1.11 后,我的代码不再允许检索字段名称(在 go1.10.3 中正常)
查看新版本的 decode.go,我未能找到针对此行为变化的简单解决方法
这是一个简单的示例,展示了该行为:
package main
import (
"encoding/json"
"fmt"
"strings"
)
type MyString string
func (m *MyString) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return &json.UnmarshalTypeError{Value: string(data), Type: reflect.TypeOf(m)}
}
*m = MyString(s)
return nil
}
type MyStruct struct {
Field MyString
}
func main() {
var m MyStruct
err := json.NewDecoder(strings.NewReader(`{"Field": 123}`)).Decode(&m)
if err != nil {
if e, ok := err.(*json.UnmarshalTypeError); ok {
fmt.Printf("Field: %s\n", e.Field)
}
}
}
这是回归问题还是不幸消失的意外行为?
感谢您的帮助
更多关于Golang中UnmarshalTypeError不再提供Field name的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢提供的链接,在提交这个求助请求之前我确实已经看到了这个问题
不幸的是,这并不完全是同一个问题
而且我测试了建议的修复方案,但这并不能恢复之前的行为
更多关于Golang中UnmarshalTypeError不再提供Field name的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我认为这个问题正在这里被跟踪:https://github.com/golang/go/issues/27275
在Go 1.11中,json.UnmarshalTypeError的Field字段确实被移除了,这是有意为之的变更。根据Go的发布说明和源代码变更,这个字段被认为是一个实现细节,不应该在公共API中暴露。
当使用自定义解组器时,如果需要获取字段名称,可以通过在自定义解组器中捕获上下文信息来实现。以下是修改后的示例代码:
package main
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
type MyString string
func (m *MyString) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
// 创建自定义错误类型来包含字段信息
return &CustomUnmarshalTypeError{
Value: string(data),
Type: reflect.TypeOf(m),
Field: "Field", // 在实际应用中需要通过其他方式获取字段名
}
}
*m = MyString(s)
return nil
}
// 自定义错误类型来保留字段信息
type CustomUnmarshalTypeError struct {
Value string
Type reflect.Type
Field string
}
func (e *CustomUnmarshalTypeError) Error() string {
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s of type %s",
e.Value, e.Field, e.Type)
}
type MyStruct struct {
Field MyString
}
func main() {
var m MyStruct
err := json.NewDecoder(strings.NewReader(`{"Field": 123}`)).Decode(&m)
if err != nil {
if e, ok := err.(*CustomUnmarshalTypeError); ok {
fmt.Printf("Field: %s\n", e.Field)
fmt.Printf("Error: %s\n", e.Error())
} else {
fmt.Printf("Other error: %s\n", err)
}
}
}
在实际应用中,可以通过以下方式在自定义解组器中获取字段名称:
package main
import (
"encoding/json"
"fmt"
"reflect"
"runtime"
"strings"
)
type FieldAwareUnmarshaler struct {
FieldName string
}
func (f *FieldAwareUnmarshaler) setFieldName(name string) {
f.FieldName = name
}
type MyString string
func (m *MyString) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
// 通过调用栈分析获取字段信息(简化示例)
return &CustomUnmarshalTypeError{
Value: string(data),
Type: reflect.TypeOf(m),
Field: getCallerField(), // 需要实现此函数来获取字段名
}
}
*m = MyString(s)
return nil
}
// 简化的字段名获取函数(实际实现会更复杂)
func getCallerField() string {
pc := make([]uintptr, 10)
runtime.Callers(2, pc)
frames := runtime.CallersFrames(pc)
for {
frame, more := frames.Next()
if strings.Contains(frame.Function, "UnmarshalJSON") {
continue
}
// 这里可以解析frame来获取字段信息
break
}
return "Field" // 返回硬编码值作为示例
}
这个变更不是回归问题,而是Go团队有意移除的实现细节。建议在需要字段信息的场景中使用自定义错误类型来替代。

