はじめに
toml便利です。
でも interface{}
型で記述したパラメータの中に配列や連想配列が入ると、要素が interface{}
型になるのが不便です。
例
type Config struct {
ConfigA string `toml:"config_a"`
ConfigB int64 `toml:"config_b"`
Options map[string]interface{} `toml:"options"`
}
config.toml
config_a = "a"
config_b = 0
[options]
opt1 = "aaa"
opt2 = ["aaa", "bbb"] # <- こいつが []interface{} 型になる
最近go製のフレームワークを作成しているのですが、バリデーションのために reflect.TypeOf(config.options["opt2"])
で型を取ってみると、冒頭で述べた通り []interface{}
型になりぐぬぬとなります。
つくったもの
interface{}
型の中に配列・連想配列を突っ込んだときにも適切に方が取れる TypeOf
関数をつくりました。
注) 直近、配列と連想配列に対応できれば十分だったので、その2つにしか対応していません。あしからず。
func typeOf(i interface{}) (reflect.Type, error) {
switch v := i.(type) {
case []interface{}:
if len(v) == 0 {
return nil, errors.New("no elements")
}
var elemType reflect.Type
for _, el := range v {
t, err := typeOf(el) // <- ここで再帰的に呼ぶ
if err != nil {
return nil, err
}
if elemType != nil && elemType != t {
return nil, errors.New("multiple element types")
} else {
elemType = t
}
}
return reflect.SliceOf(elemType), nil
case map[string]interface{}:
if len(v) == 0 {
return nil, errors.New("no elements")
}
var elemType reflect.Type
for _, el := range v {
t, err := typeOf(el) // <- ここで再帰的に呼ぶ
if err != nil {
return nil, err
}
if elemType != nil && elemType != t {
return nil, errors.New("multiple element types")
} else {
elemType = t
}
}
return reflect.MapOf(reflect.TypeOf(""), elemType), nil
default:
return reflect.TypeOf(v), nil
}
}
テストしてみる
テスト用のtomlがこれ
test.toml
var0 = [0, 0]
var1 = ["string1", "string2"]
var2 = [["string1", "string2"], ["string1", "string2"]]
var3 = [] # <- これはエラーになる
var4 = [["string1", "string2"], [0, 0]] # <- これはエラーになる
[var5]
aaa = "bbb"
ccc = "ddd"
[var6]
aaa = 0.0
bbb = 0.0
[var7]
[var7.aa]
aaa = 0.0
[var7.bb]
bbb = 0.0
[var8]
aaa = [0, 1, 2, 3]
ccc = [2, 3, 4]
[var9] # <- これはエラーになる
aaa = [0, 1, 2, 3]
ccc = ["aaa", "bbb"]
main.go
func main() {
var config Config
toml.DecodeFile("./test.toml", &config)
fmt.Println(reflect.TypeOf(config.Var0))
fmt.Println(reflect.TypeOf(config.Var1))
fmt.Println(reflect.TypeOf(config.Var2))
fmt.Println(reflect.TypeOf(config.Var3))
fmt.Println(reflect.TypeOf(config.Var4))
fmt.Println(reflect.TypeOf(config.Var5))
fmt.Println(reflect.TypeOf(config.Var6))
fmt.Println(reflect.TypeOf(config.Var7))
fmt.Println(reflect.TypeOf(config.Var8))
fmt.Println(reflect.TypeOf(config.Var9))
fmt.Println(typeOf(config.Var0))
fmt.Println(typeOf(config.Var1))
fmt.Println(typeOf(config.Var2))
fmt.Println(typeOf(config.Var3))
fmt.Println(typeOf(config.Var4))
fmt.Println(typeOf(config.Var5))
fmt.Println(typeOf(config.Var6))
fmt.Println(typeOf(config.Var7))
fmt.Println(typeOf(config.Var8))
fmt.Println(typeOf(config.Var9))
}
結果
[]interface {}
[]interface {}
[]interface {}
[]interface {}
[]interface {}
map[string]interface {}
map[string]interface {}
map[string]interface {}
map[string]interface {}
map[string]interface {}
[]int64 <nil>
[]string <nil>
[][]string <nil>
<nil> no elements # <- elementが無いので判別できない
<nil> multiple element types # <- 入れ子内のtypeが複数あるのでエラー
map[string]string <nil>
map[string]float64 <nil>
map[string]map[string]float64 <nil>
map[string][]int64 <nil>
<nil> multiple element types # <- 入れ子内のtypeが複数あるのでエラー