如何用github.com/mitchellh/mapstructure将结构体编码为指定格式map?
解决mapstructure将timestamppb.Timestamp转为字符串格式的问题
问题根源
你之前的错误是混淆了mapstructure的**解码(Decode)与编码(Encode)**方向:默认的DecodeHook用于将map转成结构体的场景,而你需要把结构体转成map,属于编码流程,必须用EncodeHook处理类型转换,同时覆盖timestamppb.Timestamp默认转map的行为。
实现方案
通过EncodeHookFunc结合反射,实现*timestamppb.Timestamp字段转指定格式字符串的逻辑,完整代码如下:
package main import ( "fmt" "reflect" "time" "github.com/golang/protobuf/ptypes/timestamp" "github.com/mitchellh/mapstructure" ) type Struct struct { CreateDate *timestamp.Timestamp `mapstructure:"create_date"` } // TimestampToStringHook 将*timestamppb.Timestamp格式化为"2006-01-02 15:04:05"字符串 func TimestampToStringHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { // 仅处理从*timestamppb.Timestamp到string的转换(编码方向) if f == reflect.TypeOf(×tamp.Timestamp{}) && t.Kind() == reflect.String { ts := data.(*timestamp.Timestamp) return ts.AsTime().Format("2006-01-02 15:04:05"), nil } // 其他类型不做处理,返回原数据 return data, nil } func main() { // 构造测试结构体实例 s := Struct{ CreateDate: ×tamp.Timestamp{ Seconds: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC).Unix(), }, } // 配置解码器,核心是设置EncodeHook var result map[string]any config := &mapstructure.DecoderConfig{ Result: &result, TagName: "mapstructure", EncodeHook: mapstructure.ComposeEncodeHookFunc( TimestampToStringHook, ), } decoder, err := mapstructure.NewDecoder(config) if err != nil { panic(err) } // 执行结构体到map的转换 if err := decoder.Decode(s); err != nil { panic(err) } fmt.Printf("%#v\n", result) // 输出:map[string]interface {}{"create_date":"2024-01-01 12:00:00"} }
关键说明
- 钩子方向匹配:通过判断源类型为
*timestamppb.Timestamp、目标类型为string,确保只处理我们需要的转换场景,不干扰其他类型的默认处理。 - EncodeHook的作用:
DecoderConfig中的EncodeHook专门负责结构体转map时的类型转换,而你之前误用的DecodeHook是map转结构体时的钩子,这是报错的核心原因。 - 时间格式化逻辑:借助
timestamppb.Timestamp的AsTime()方法转成Go标准time.Time类型,再用Format()生成指定格式的字符串。
内容的提问来源于stack exchange,提问作者MrDan4es
相关产品推荐
相关产品推荐

