🚙

[golang]構造体をmap[string]interface{}に変換する

2021/10/16に公開

はじめに

手打ちでmap[string]interface{}作るのだるって思ったので、自動で構造体から生成できる方法を調べてみました。
UnexportedField以外要素からmapを作る部分に苦労しました。
また、構造体が入れ子になっている場合に関しても苦労しました。

json.Marshalとjson.Unmarshalを利用する

結論から言うと、こちらの方法で解決しました。
はじめにで述べた、苦労した2点が出来るようになっています。
コードは以下です。

struct.go
type PrimaryID struct {
	ID     int32  `json:"-"`
	HashID string `json:"hash_id"`
}
type User struct {
	tableName struct{}
	PrimaryID
	Name string `json:"name"`
}
json.go
func StructToJsonTagMap(data interface{}) (map[string]interface{}, error) {
	jsonStr, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	out := new(bytes.Buffer)
	// JSONの整形
	err = json.Indent(out, jsonStr, "", "    ")
	if err != nil {
		return nil, err
	}
	var mapData map[string]interface{}
	if err := json.Unmarshal([]byte(out.String()), &mapData); err != nil {
		return nil, err
	}
	return mapData, err
}

[WIP]構造体から直接map[string]interface{}を作成する

こちらでも出来るはずですが、僕は諦めました。
入れ子になった構造体からmapを作成できる方いませんか???
コメントで教えていただけると助かります。

json.go
func StructToJsonTagMap(data interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	elem := reflect.ValueOf(data).Elem()
	size := elem.NumField()
	//最初はテーブル名のため i=1から
	for i := 1; i < size; i++ {
		field := elem.Type().Field(i).Tag.Get("json")
		if field == "-" {
			continue
		}
		value := elem.Field(i).Interface()
		result[field] = value
	}
	return result
}

[WIP]tagでkeyを指定しない場合

json.go
func StructToMap(data interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	elem := reflect.ValueOf(data).Elem()
	size := elem.NumField()

	for i := 0; i < size; i++ {
		field := elem.Type().Field(i).Name
		value := elem.Field(i).Interface()
		result[field] = value
	}
	return result
}

環境

  • MacBook Air (M1, 2020)
  • go -v 1.15
  • echo -v 4.3.0
  • Docker version 20.10.8, build 3967b7d
  • docker-compose version 1.29.2, build 5becea4c

参考

Discussion