在Go中自定义配置加载

时间:2018-05-01 06:41:20

标签: json go config

我正在使用JSON文件来存储/加载我的配置。假设我有以下内容:

type X interface

// implements interface X
type Y struct {
    Value string
}

// implements interface X
type Z struct {
    Value string
}

type Config struct {
    interfaceInstance X `json:"X"`
}

配置文件示例:

{
  "config1": {
    "X": {
      "type": "Z",
      "Value": "value_1"
    }
  },


  "config2": {
    "X": {
      "type": "Y",
      "Value": "value_2"
    }
  }
}

我希望能够像本示例那样定义配置文件,并能够将JSON动态加载为struct Y或struct Z。有关如何完成此任务的任何建议?我正在使用一个简单的json.Decoder来加载JSON作为结构。

decoder := json.NewDecoder(file)
err = decoder.Decode(&config)

1 个答案:

答案 0 :(得分:0)

一种可行的策略是为json.Unmarshaler类型实现Config,使您首先解组到一个通用对象并检查“ type”属性,然后打开类型字符串,将相同的字节数组解组为已知类型,然后分配给配置的“ interfaceInstance”成员。

例如(Go Playground):

// Note the slightly different JSON here...
var jsonstr = `{
  "config1": {
    "type": "Z",
    "Value": "value_1"
  },
  "config2": {
    "type": "Y",
    "Value": "value_2"
  }
}`

func main() {
  config := map[string]Config{}
  err := json.Unmarshal([]byte(jsonstr), &config)
  if err != nil {
    panic(err)
  }

  fmt.Printf("OK: %#v\n", config)
  // OK: map[string]main.Config{
  //   "config1": main.Config{interfaceInstance:main.Z{Value:"value_1"}},
  //   "config2": main.Config{interfaceInstance:main.Y{Value:"value_2"}},
  // }
}

func (c *Config) UnmarshalJSON(bs []byte) error {
  // Unmarshal into an object to inspect the type.
  var obj map[string]interface{}
  err := json.Unmarshal(bs, &obj)
  if err != nil {
    return err
  }

  // Unmarshal again into the target type.
  configType := obj["type"].(string)
  switch configType {
  case "Y":
    var y Y
    if err = json.Unmarshal(bs, &y); err == nil {
      c.interfaceInstance = y
    }
  case "Z":
    var z Z
    if err = json.Unmarshal(bs, &z); err == nil {
      c.interfaceInstance = z
    }
  default:
    return fmt.Errorf("unexpected type %q", configType)
  }
  return err
}