【问题标题】:Fill a struct from a map with reflection用反射填充映射中的结构
【发布时间】:2026-02-17 03:35:02
【问题描述】:

我有一张这样的地图 type Store map[string]string

从这张地图,我想填充一个结构。目标是,对于结构体的每个字段,找到与字段名匹配的键,取值,根据结构体字段的类型,将值转换为合适的结构体字段类型并设置。

基本上,地图将包含整数、布尔值、字符串和持续时间作为字符串,因此转换应该只是strconv.atoi()time.parseDuration() ...

另外,我想使用 structs 标签来指定 map 中键的名称,因为 struct 字段可能是 camelCase,而 map 中的键将是这样的“example_key”

知道如何做到这一点。我已经阅读了有关 golang 反射的信息,但它对我来说仍然是不透明的。我只需要一个解释来解决这个问题,然后我想我可以自己处理实现。

谢谢

【问题讨论】:

  • 这应该是一种适用于一般情况的方法吗?如果没有,约定是在包中定义一个构造函数,在那里进行赋值并返回一个新实例。就像func NewStoreObject(store map[string]string) StoreObject, error。如果你正在寻找一个通用的解决方案,你会问很多。但我想我也可以给出一个解释。它只是反映结构上的类型,根据字段名称索引映射,打开结构字段类型,在每种情况下您都有代码来进行转换和赋值。
  • @evanmcdonnal 你好!这不是针对一般用例,而是针对特定用例。我现在将确切地在结构中使用哪些类型,所以只会对这些类型进行转换,但我仍然需要测试 reflect.Type 我猜。该地图不会作为参数传递,它来自其他地方,但您建议的原型是我的想法。
  • 嗯,这两种范式都不适合。如果您使用反射,我希望您使用它来决定在运行时在哪些字段上设置哪些值。如果您编写的代码在编译时显示为thisType.Name = storeMap["name"],那么您不妨使用我描述的构造函数类型范例,因为您将对每种类型都有一个特殊情况,就像它们都有自己的构造函数方法一样映射并返回实例。如果你想要像ConvertoObject(store Store) interface{} 这样的单一方法,那么使用反射是合理的。
  • @evanmcdonnal 我明白你的意思。我认为这比编写一个大的构造函数块要“干净”得多。您能否继续使用一般方法扩展反射解决方案,以便我了解这一点?

标签: reflection go


【解决方案1】:

为了避免在 cmets 中进行一些扩展的讨论,我将给出一个答案,解释我会选择的两种方法。一般来说,我会避免使用反射方法。在某些情况下会调用它,但大多数情况下,更冗长、更文字、更简单的代码会更好,即使它多几行并且方法之间存在一些重叠。

所以,从简单的例子开始。假设你有 3 种类型,GroceryStoreComputerStoreCornerStore。在每种类型定义的任何包中,定义一些构造函数之类的方法是相当普遍的(从技术上讲,它们不是构造函数,只是包范围内的方法来实例化一个类型并返回它,但目的相同)。所以一个例子是;

func NewComputerStore(store Store) *ComputerStore {
      return &ComputerStore{
            Name: store["Name"],
            Location: store["Location"],
            //ect
      }
}

当然,上面的例子是完全不安全的。实际上,您需要使用 v, ok := myMape["key"] 语法,然后分配 if ok,如果不是,则执行任何操作(可能是分配默认值、记录错误和抛出错误的某种组合)。

现在,如果您想要一个通用的方法...它通常看起来更像这样(注意这是未经测试的,并且是伪代码而不是工作示例);

func (s *Store) ConvertToObject(obj interface{}) error {
    val := reflect.ValueOf(obj)
    for i := 0; i < val.NumField(); i++ {
        switch v := val.Field(i).(type) {
        case int64:
            val.Field(i).SetInt(strconv.Atoi(s[val.Field(i).Name]))
        }
    }
}

所以上面代码的基本思想是这样的。您从商店地图开始。调用者知道该映射应该是什么类型。他们调用此方法传递该类型的新实例。在方法中,我们并不真正关心类型是什么,我们关心它的字段的类型是什么。所以我迭代传入的对象上的所有字段。每个字段都通过类型切换(因为对于该方法应该使用的结构中存在的每种类型,我都需要一个字符串到 X 的转换,或者至少一个体面的基本情况),在每种类型的情况下,您可以使用字段名称进行分配,以在 Store 映射中查找其值并将字符串转换为 X。

希望这是有道理的。如果它没有真正阐明方法,我可以跟进。请记住,上面的代码不打算运行。另外,我忽略了所有错误处理,您需要 lots 来使通用函数工作(如果转换失败怎么办?如果类型无法识别怎么办?其他所有可能的情况呢?不常见的错误?)。

【讨论】:

  • 非常感谢您的明确答复。我理解了这种方法,而且这种解释在我的脑海中更加简洁。
  • RE:构建一个通用方法 - 使用类似 github.com/mitchellh/mapstructure 的方法可能更明智,而不是自己滚动。使用反射包可能很脆弱(由于其性质)。
  • @elithrar 这看起来正是我所需要的。我会关注这一点以增加接受的答案。谢谢!