Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to skip existing fields when unmarshal json in golang?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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}

>Solution :

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}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading