Unmarshal json string to a struct that have one element of the struct itself

匿名 (未验证) 提交于 2019-12-03 01:58:03

问题:

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

{     []} 

回答1:

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:[]} 


回答2:

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



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!