I am go beginner and trying to unmarshal the following json string
[{ "db": { "url": "mongodb://localhost", "port": "27000", "uname": "", "pass": "", "authdb": "", "replicas": [ { "rs01": { "url":"mongodb://localhost", "port": "27001", "uname": "", "pass": "", "authdb": "" } }, { "rs02": { "url":"mongodb://localhost", "port": "27002", "uname": "", "pass": "", "authdb": "" } } ] } }]
here is the struct
type DBS struct { URL string `json:url` Port string `json:port` Uname string `json:uname` Pass string `json:pass` Authdb string `json:authdb` Replicas []DBS `json:replicas` }
and here is the function
func loadConfigs() []DBS { var config []DBS raw, err := ioutil.ReadFile("./config.json") if err != nil { fmt.Println(err.Error()) os.Exit(1) } json.Unmarshal(raw, &config) return config }
The function is returning
{ []}
Your JSON input is not a slice of DBS
, as there is another JSON object wrapper, and a value of DBS
belongs to the property "db"
.
Going deeper, the "replicaps"
is a JSON array with objects holding varying keys, their values being representable by DBS
.
So to fully describe your JSON, you need some kind of "dynamic" type. A map is such a dynamic type for example.
So your original JSON input can be fully modeled with the type: []map[string]DBS
. It's a slice of maps, as your JSON input contains a JSON array. And the map key can model any property name, and the value is a JSON object modeled by the DBS
struct.
See this example which fully parses the JSON input:
type DBS struct { URL string `json:"url"` Port string `json:"port"` Uname string `json:"uname"` Pass string `json:"pass"` Authdb string `json:"authdb"` Replicas []map[string]DBS `json:"replicas"` } func main() { var dbs []map[string]DBS if err := json.Unmarshal([]byte(src), &dbs); err != nil { panic(err) } fmt.Printf("%+v", dbs) }
Note the proper tag syntax (e.g. json:"url"
).
Output (try it on the Go Playground):
[map[db:{URL:mongodb://localhost Port:27000 Uname: Pass: Authdb: Replicas:[map[rs01:{URL:mongodb://localhost Port:27001 Uname: Pass: Authdb: Replicas:[]}] map[rs02:{URL:mongodb://localhost Port:27002 Uname: Pass: Authdb: Replicas:[]}]]}]]
Note that you can further model the first level which is always "db"
, and we can switch to pointers (I used non-pointers in the first example so the printed result is readable):
type DBReplicated struct { DB *DBS `json:"db"` } type DBS struct { URL string `json:"url"` Port string `json:"port"` Uname string `json:"uname"` Pass string `json:"pass"` Authdb string `json:"authdb"` Replicas []map[string]*DBS `json:"replicas"` } func main() { var dbs []*DBReplicated if err := json.Unmarshal([]byte(src), &dbs); err != nil { panic(err) } db := dbs[0].DB fmt.Printf("%+v\n", db) for _, dbs := range db.Replicas { for name, replica := range dbs { fmt.Printf("%s: %+v\n", name, replica) } } }
Output (try it on the Go Playground):
&{URL:mongodb://localhost Port:27000 Uname: Pass: Authdb: Replicas:[map[rs01:0x10538200] map[rs02:0x10538240]]} rs01: &{URL:mongodb://localhost Port:27001 Uname: Pass: Authdb: Replicas:[]} rs02: &{URL:mongodb://localhost Port:27002 Uname: Pass: Authdb: Replicas:[]}
You can also write your own json.Unmarshaler
implementation if, for some reason, you're stuck with the already existing DBS
structure.
(In the below example I do actually change the structure a little to keep track of the name/key but that's optional, it's not necessary to make the unmarshaling work.)
type DBS struct { name string URL string `json:url` Port string `json:port` Uname string `json:uname` Pass string `json:pass` Authdb string `json:authdb` Replicas []DBS `json:replicas` } func (db *DBS) UnmarshalJSON(data []byte) error { raw := map[string]json.RawMessage{} if err := json.Unmarshal(data, &raw); err != nil { return err } if len(raw) > 1 { return fmt.Errorf("fail") } type _DBS DBS _db := (*_DBS)(db) for name, v := range raw { db.name = name return json.Unmarshal(v, _db) } return nil }
https://play.golang.org/p/c288n7holS