Home > Net >  How to skip existing fields when unmarshal json in golang?
How to skip existing fields when unmarshal json in golang?

Time:05-23

I use json file to configure my program arguments and use flag package to configure the same arguments.

When some argument parsed by json file and flag at the same time, I wish use the arguments parsed through flag.

The trouble is that the json file path is also parsed through flag. The json path can be obtained after calling flag.parse(), but the arguments is also parsed, then Unmarshal json will overwrite the arguments of flag parsing.

example json:

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

example code:

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)
}

program example output:
./foo.exe -opt2 world
out: {Opt1:0 Opt2:world}

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

./foo.exe -config config.json -opt2 world
real out: {Opt1:1 Opt2:hello}
hope out: {Opt1:1 Opt2:world}

CodePudding user response:

One easy solution would be to first parse the JSON config file, and once that's done, then proceed to parse CLI arguments.

The problem with this is that the JSON config file is also a CLI arg.

The simplest solution would be to call flag.Parse() again after parsing the config file:

// 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()
}

The second flag.Parse() will override and thus have precedence over data coming from the JSON file.

With this addition, running

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

Output will be what you desire:

{Opt1:1 Opt2:world}
  • Related