Unmarshaling nested JSON objects in Golang

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

问题:

There are a few questions on the topic but none of them seem to cover my case, thus I'm creating a new one.

I have JSON like the following:

{"foo":{ "bar": "1", "baz": "2" }, "more": "text"} 

Is there a way to unmarshal the nested bar property and assign it directly to a struct property without creating a nested struct?

The solution I'm adopting right now is the following:

type Foo struct {     More String `json:"more"`     Foo  struct {         Bar string `json:"bar"`         Baz string `json:"baz"`     } `json:"foo"`     //  FooBar  string `json:"foo.bar"` } 

This is a simplified version, please ignore the verbosity. As you can see, I'd like to be able to parse and assign the value to

//  FooBar  string `json:"foo.bar"` 

I've seen people using a map, but that's not my case. I basically don't care about the content of foo (which is a large object), except for a few specific elements.

What is the correct approach in this case? I'm not looking for weird hacks, thus if this is the way to go, I'm fine with that.

回答1:

Is there a way to unmarshal the nested bar property and assign it directly to a struct property without creating a nested struct?

No, encoding/json cannot do the trick with ">some>deep>childnode" like encoding/xml can do. Nested structs is the way to go.



回答2:

Like what Volker mentioned, nested structs is the way to go. But if you really do not want nested structs, you can override the UnmarshalJSON func.

http://play.golang.org/p/T0aZEDL0Nu

type A struct {     FooBar string // takes foo.bar     FooBaz string // takes foo.baz     More   string `json:"more"` }  func (a *A) UnmarshalJSON(b []byte) error {     var f interface{}     json.Unmarshal(b, &f)      m := f.(map[string]interface{})      foomap := m["foo"]     v := foomap.(map[string]interface{})      a.FooBar = v["bar"].(string)     a.FooBaz = v["baz"].(string)      return nil } 

Please ignore the fact that I'm not returning a proper error. I left that out for simplicity.



回答3:

This is an example of how to unmarshall JSON responses from the Safebrowsing v4 API sbserver proxy server: https://play.golang.org/p/4rGB5da0Lt

// this example shows how to unmarshall JSON requests from the Safebrowsing v4 sbserver package main  import (     "fmt"     "log"     "encoding/json" )  // response from sbserver POST request type Results struct {     Matches []Match      }  // nested within sbserver response type Match struct {     ThreatType string      PlatformType string      ThreatEntryType string      Threat struct {         URL string     } }  func main() {     fmt.Println("Hello, playground")      // sample POST request     //   curl -X POST -H 'Content-Type: application/json'      // -d '{"threatInfo": {"threatEntries": [{"url": "http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}]}}'      // http://127.0.0.1:8080/v4/threatMatches:find      // sample JSON response     jsonResponse := `{"matches":[{"threatType":"MALWARE","platformType":"ANY_PLATFORM","threatEntryType":"URL","threat":{"url":"http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}}]}`      res := &Results{}     err := json.Unmarshal([]byte(jsonResponse), res)         if(err!=nil) {             log.Fatal(err)         }      fmt.Printf("%v\n",res)     fmt.Printf("\tThreat Type: %s\n",res.Matches[0].ThreatType)     fmt.Printf("\tPlatform Type: %s\n",res.Matches[0].PlatformType)     fmt.Printf("\tThreat Entry Type: %s\n",res.Matches[0].ThreatEntryType)     fmt.Printf("\tURL: %s\n",res.Matches[0].Threat.URL) } 


回答4:

Yes. With gjson all you have to do now is:

bar := gjson.Get(json, "foo.bar")

bar could be a struct property if you like. Also, no maps.



回答5:

What about anonymous fields? I'm not sure if that will constitute a "nested struct" but it's cleaner than having a nested struct declaration. What if you want to reuse the nested element elsewhere?

type NestedElement struct{     someNumber int `json:"number"`     someString string `json:"string"` }  type BaseElement struct {     NestedElement `json:"bar"` } 


回答6:

yes you can assign the values of nested json to struct until you know the underlying type of json keys for example.

package main  import (     "encoding/json"     "fmt" )  // Object type Object struct {     Foo map[string]map[string]string `json:"foo"`     More string `json:"more"` }  func main(){     someJSONString := []byte(`{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}`)     var obj Object     err := json.Unmarshal(someJSONString, &obj)     if err != nil{         fmt.Println(err)     }     fmt.Println("jsonObj", obj) } 


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