Search code examples
gogo-cobraviper-go

Unable to unmarshal using viper


I've been trying to extract some JSON by unmarshalling my json file but, I have no idea why it is not happening. I am able to fetch the data using viper.AllSettings() but not by unmarshal. I think i am making a silly mistake, please share your thoughts on the same. The github link is - https://github.com/parthw/100-days-of-code/tree/main/golang/d6-cobra-viper-continued and the code is as follows.

main.go

package main

import (
    "fmt"

    "example.com/cobra-viper/cmd"
    "github.com/spf13/viper"
)

// Myconfig example
type Myconfig struct {
    username string `mapstructure:"username"`
}

func main() {
    cmd.Execute()
    fmt.Println("I can print this ", viper.AllSettings())
    var mc Myconfig
    if err := viper.Unmarshal(&mc); err != nil {
        fmt.Println(err)
    }
    fmt.Println(mc)
}

code generated using cobra CLI in cmd directory:

package cmd

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"

    homedir "github.com/mitchellh/go-homedir"
    "github.com/spf13/viper"
)

var (
    cfgFile string
    author  string
)

// Myconfig example
type Myconfig struct {
    username string
}

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
    Use:   "cobra-viper",
    Short: "A brief description of your application",
    Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
    // Uncomment the following line if your bare application
    // has an action associated with it:
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("Welcome to rootcmd")

        var mc Myconfig
        if err := viper.Unmarshal(&mc); err != nil {
            fmt.Println(err)
        }
        fmt.Println(mc)
        fmt.Println("I can print this ", viper.AllSettings())
    },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println("I can print this ", viper.AllSettings())
}

func init() {
    cobra.OnInitialize(initConfig)

    // Here you will define your flags and configuration settings.
    // Cobra supports persistent flags, which, if defined here,
    // will be global for your application.

    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra-viper.json)")
    //rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")

    // Cobra also supports local flags, which will only run
    // when this action is called directly.
    //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
    //viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))

}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
    if cfgFile != "" {
        // Use config file from the flag.
        viper.SetConfigFile(cfgFile)
    } else {
        // Find home directory.
        home, err := homedir.Dir()
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        // Search config in home directory with name ".cobra-viper" (without extension).
        viper.AddConfigPath(home)
        viper.SetConfigName(".cobra-viper")
    }

    viper.AutomaticEnv() // read in environment variables that match

    // If a config file is found, read it in.
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }

}

Output:

~/Documents/personal-git/100-days-of-code/golang/d6-cobra-viper-continued(main*) » go run main.go                                                                                   
Using config file: /Users/parth/.cobra-viper.json
Welcome to rootcmd
{}
I can print this  map[username:parth-wadhwa]
I can print this  map[username:parth-wadhwa]
I can print this  map[username:parth-wadhwa]
{}

JSON file:

~/Documents/personal-git/100-days-of-code/golang/d6-cobra-viper-continued(main*) » cat /Users/parth/.cobra-viper.json                                                              

{
  "username": "parth-wadhwa"
}

Solution

  • Your problem trivially comes down to the fact, if the username field in your MyConfig struct is exported or not. It needs to uppercased to be "exported" for Unmarshal to decode the value into the struct.

    type Myconfig struct {
        Username string `mapstructure:"username"`
    }
    

    You can look at JSON and dealing with unexported fields to understand more on why the json package needs it.