Go语言使用反射动态创建结构体无法完成XML反序列化报错排查
问题原因
- 动态构造的结构体字段被识别为非导出字段:构造
reflect.StructField时你设置了非空的PkgPath属性,Go语言中只要PkgPath不为空的字段,哪怕字段名首字母大写,也会被判定为包内私有(非导出)字段。XML反序列化包无法读写非导出字段,后续调用Interface()方法读取字段值时就会触发你遇到的panic。 - 反射实例不可写入:你使用
reflect.Zero()创建的是不可寻址、不可修改的结构体零值,直接传地址给xml.Unmarshal也无法正常写入解析结果。 - 字段索引逻辑错误:循环填充
values切片时固定取第0个字段Field(0),无法正确获取所有字段的值。
解决方法
修改后的完整代码如下:
func FindAttrs(attrs []Tag, errorChan chan<- error) []interface{} { var tableFields []reflect.StructField for _, v := range attrs { tableFields = append(tableFields, reflect.StructField{ Name: strings.Title(v.Name), Type: reflect.TypeOf(""), Tag: reflect.StructTag(fmt.Sprintf(`xml:"%v,attr"`, v.Name)), // 去掉PkgPath赋值,空值代表该字段为导出字段 }) } structType := reflect.StructOf(tableFields) // 使用reflect.New创建可寻址的结构体指针,支持写入操作 structPtr := reflect.New(structType) // 直接传指针的interface{}给反序列化方法 err := xml.Unmarshal(ReadBytes(errorChan), structPtr.Interface()) HandleError(err, "Error parse config", false, errorChan) // 获取指针指向的实际结构体实例 unmarshalStruct := structPtr.Elem() values := make([]interface{}, unmarshalStruct.NumField()) for i := 0; i < unmarshalStruct.NumField(); i++ { // 按索引取对应字段的值 values[i] = unmarshalStruct.Field(i).Interface() } return values }
核心改动说明
- 移除
StructField的PkgPath赋值,空值的PkgPath会让Go将字段识别为导出字段,反射读写和XML反序列化都可以正常执行 - 用
reflect.New替代reflect.Zero创建结构体实例,获得可写入的指针类型值,保证反序列化结果可以正常写入 - 修正循环内的字段索引,按下标读取对应字段值填充切片
内容的提问来源于stack exchange,提问作者Intolighter
相关产品推荐
相关产品推荐

