在 golang 中解组 json 时如何跳过现有字段?

How to skip existing fields when unmarshal json in golang?

我使用 json 文件来配置我的程序参数,并使用 flag 包来配置相同的参数。

json文件和flag同时解析某些参数时,我希望使用flag解析的参数。

麻烦的是json文件路径也是通过flag解析出来的。 调用flag.parse()后可以得到json路径,但是也会解析参数,那么Unmarshaljson会覆盖flag解析的参数。

示例json:

{
    "opt1": 1,
    "opt2": "hello"
}

示例代码:

var Config = struct {
    Opt1 int    `json:"opt1"`
    Opt2 string `json:"opt2"`
}{
    Opt1: 0,
    Opt2: "none",
}

func main() {

    // parse config file path
    var configFile string
    flag.StringVar(&configFile, "config", "", "config file path")

    // parse options
    flag.IntVar(&Config.Opt1, "opt1", Config.Opt1, "")
    flag.StringVar(&Config.Opt2, "opt2", Config.Opt2, "")

    // parse flags
    flag.Parse()

    // load config options from config.json file
    if configFile != "" {
        if data, err := ioutil.ReadFile(configFile); err != nil {
            fmt.Printf("read config file error: %v\n", err)
        } else if err = json.Unmarshal(data, &Config); err != nil {
            fmt.Printf("parse config file error: %v\n", err)
        }
    }

    fmt.Printf("%+v", Config)
}

程序示例输出:
./foo.exe -opt2 world
输出:{Opt1:0 Opt2:world}

./foo.exe -config config.json
输出:{Opt1:1 Opt2:hello}

./foo.exe -config config.json -opt2 world
实出:{Opt1:1 Opt2:hello}
希望出来:{Opt1:1 Opt2:world}

一个简单的解决方案是首先解析 JSON 配置文件,完成后,再继续解析 CLI 参数。

问题在于 JSON 配置文件也是一个 CLI 参数。

最简单的解决方案是在解析配置文件后再次调用 flag.Parse()

// load config options from config.json file
if configFile != "" {
    if data, err := ioutil.ReadFile(configFile); err != nil {
        fmt.Printf("read config file error: %v\n", err)
    } else if err = json.Unmarshal(data, &Config); err != nil {
        fmt.Printf("parse config file error: %v\n", err)
    }

    // parse flags again to have precedence
    flag.Parse()
}

第二个 flag.Parse() 将覆盖并因此优先于来自 JSON 文件的数据。

加上这个,运行

./foo.exe -config config.json -opt2 world

输出将是你想要的:

{Opt1:1 Opt2:world}