Unmarshal nested JSON structure - json

http://play.golang.org/p/f6ilWnWTjm
I am trying to decode the following string but only getting null values.
How do I decode nested JSON structure in Go?
I want to convert the following to map data structure.
package main
import (
"encoding/json"
"fmt"
)
func main() {
jStr := `
{
"AAA": {
"assdfdff": ["asdf"],
"fdsfa": ["1231", "123"]
}
}
`
type Container struct {
Key string `json:"AAA"`
}
var cont Container
json.Unmarshal([]byte(jStr), &cont)
fmt.Println(cont)
}

Use nested structs in Go to match the nested structure in JSON.
Here's one example of how to handle your example JSON:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
jStr := `
{
"AAA": {
"assdfdff": ["asdf"],
"fdsfa": ["1231", "123"]
}
}
`
type Inner struct {
Key2 []string `json:"assdfdff"`
Key3 []string `json:"fdsfa"`
}
type Container struct {
Key Inner `json:"AAA"`
}
var cont Container
if err := json.Unmarshal([]byte(jStr), &cont); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", cont)
}
playground link
You can also use an anonymous type for the inner struct:
type Container struct {
Key struct {
Key2 []string `json:"assdfdff"`
Key3 []string `json:"fdsfa"`
} `json:"AAA"`
}
playground link
or both the outer and inner structs:
var cont struct {
Key struct {
Key2 []string `json:"assdfdff"`
Key3 []string `json:"fdsfa"`
} `json:"AAA"`
}
playground link
If you don't know the field names in the inner structure, then use a map:
type Container struct {
Key map[string][]string `json:"AAA"`
}
http://play.golang.org/p/gwugHlCPLK
There are more options. Hopefully this gets you on the right track.

First of all: Don't ignore errors returned by a function or method unless you have a very good reason to do so.
If you make the following change to the code:
err := json.Unmarshal([]byte(jStr), &cont)
if err != nil {
fmt.Println(err)
}
You will see the error message telling you why the value is empty:
json: cannot unmarshal object into Go value of type string
Simply put: Key cannot be of type string, so you have to use a different type. You have several option on how to decode it depending on the characteristics of the Key value:
If the structure is static, use another struct to match this structure.
If the structure is dynamic, use interface{} (or map[string]interface{} if it is always of type JSON object)
If the value is not to be accessed, only to be used in later JSON encoding, or if the decoding is to be delayed, use json.RawMessage

Related

How to serialize a dictionary in golang

I try to replicate this body form in order to use it in a request:
{"Responses":[{"type":"DROP_DOWN","value":"0"}]}
so what im doing is :
type FruitBasket struct {
Name5 []string `json:"Responses"`
}
form := payload{
Name5: []string{"type", "value"},
}
jsonData, err := json.Marshal(form)
fmt.Println(string(jsonData))
But i can't find a way to complete the body in the brackets
You need to use the Unmarshal function from "encoding/json" package and use a dummy struct to extract the slice fields
// You can edit this code!
// Click here and start typing.
package main
import (
"encoding/json"
"fmt"
)
func main() {
str := `{"Responses":[{"type":"DROP_DOWN","value":"0"}]}`
type Responses struct {
Type string `json:"type"`
Value string `json:"value"`
}
// add dummy struct to hold responses
type Dummy struct {
Responses []Responses `json:"Responses"`
}
var res Dummy
err := json.Unmarshal([]byte(str), &res)
if err != nil {
panic(err)
}
fmt.Println("%v", len(res.Responses))
fmt.Println("%s", res.Responses[0].Type)
fmt.Println("%s", res.Responses[0].Value)
}
JSON-to-go is a good online resource to craft Go date types for a particular JSON schema.
Pasting your JSON body & extracting out the nested types you could use the following types to generate the desired JSON schema:
// types to produce JSON:
//
// {"Responses":[{"type":"DROP_DOWN","value":"0"}]}
type FruitBasket struct {
Response []Attr `json:"Responses"`
}
type Attr struct {
Type string `json:"type"`
Value string `json:"value"`
}
to use:
form := FruitBasket{
Response: []Attr{
{
Type: "DROP_DOWN",
Value: "0",
},
}
}
jsonData, err := json.Marshal(form)
working example: https://go.dev/play/p/SSWqnyVtVhF
output:
{"Responses":[{"type":"DROP_DOWN","value":"0"}]}
Your struct is not correct. Your title want dictionary, but you write an array or slice of string.
Change your FruitBasket struct from this:
type FruitBasket struct {
Name5 []string `json:"Responses"`
}
to this
type FruitBasket struct {
Name5 []map[string]interface{} `json:"Responses"`
}
map[string]interface{} is dictionary in go
here's the playground https://go.dev/play/p/xRSDGdZYfRN

go unmarshaling nested json from http.get

I have read Unmarshaling nested JSON objects
but I am still unsure how to handle the json:
{
"input":{
"lat":1234,
"lon":1234
},
"stuff":[
{
"soc":"510950802051011",
"bbox":[
-76.743917,
37.298812,
-76.741184,
37.300357
],
"ccn":"51095",
"name":"James",
"age":"51",
"gf":"Mary",
"state":"NYC",
"pea":"PEA033",
"rea":"REA002",
"rpc":"RPC002",
"vpc":"VPC002"
}
]
}
I would like to only access stuff.ccn, stuff.name
package main
import (
"encoding/json"
"fmt"
)
func main() {
jStr := `{
"input":{
"lat":1234,
"lon":1234
},
"stuff":[
{
"soc":"510950802051011",
"bbox":[
-76.743917,
37.298812,
-76.741184,
37.300357
],
"ccn":"51095",
"name":"James",
"age":"51",
"gf":"Mary",
"state":"NYC",
"pea":"PEA033",
"rea":"REA002",
"rpc":"RPC002",
"vpc":"VPC002"
}
]
}`
type Inner struct {
Key2 []string `json:"ccn"`
Key3 []string `json:"name"`
}
type Outer struct {
Key Inner `json:"stuff"`
}
var cont Outer
json.Unmarshal([]byte(jStr), &cont)
fmt.Printf("%+v\n", cont)
}
I think the issue I am having is with the array.
Your structs needs to follow what you have in JSON. In provided jStr where top level object maps to Outer you seem to have key stuff which is an array of Inner objects. You need to modify your types to reflect that like this:
type Inner struct {
Key2 string `json:"ccn"`
Key3 string `json:"name"`
}
type Outer struct {
Key []Inner `json:"stuff"`
}
This basically says when stuff found take it as array and unmarshall each item as Inner.
You can use json-to-go-struct one to get the go struct for your json.
Or simply use var cont map[string]interface{}

Parsing dynamic json in go

I am trying to parse the following json structure, where the fields marked with "val1" and "val2" are constantly changing, so I cannot use a predefined struct. How could I parse this json in a way to be able to loop through every single "val"? Thank you!
{"result":true,"info":{"funds":{"borrow":{"val1":"0","val2":"0"},"free":{"val1":"0","val2":"0"},"freezed":{"val1":"0","val2":"0"}}}}
By unmarshalling into the following struct I can loop through the desired fields.
type Fields struct {
Result bool `json:"result"`
Info struct {
Funds struct {
Borrow, Free, Freezed map[string]interface{}
} `json:"funds"`
} `json:"info"`
}
package main
import (
"fmt"
"encoding/json"
)
type Root struct {
Result bool `json:"result"`
Info Info `json:"info"`
}
type Info struct {
Funds struct {
Borrow, Free, Freezed map[string]interface{}
} `json:"funds"`
}
func main() {
var rootObject Root
jsonContent := " {\"result\":true,\"info\":{\"funds\":{\"borrow\":{\"val1\":\"0\",\"val2\":\"0\"},\"free\":{\"val1\":\"0\",\"val2\":\"0\"},\"freezed\":{\"val1\":\"0\",\"val2\":\"0\"}}}}"
if err := json.Unmarshal([]byte(jsonContent), &rootObject); err != nil {
panic(err)
}
fmt.Println(rootObject)
}

Why doesn't JSON parsing fail with completely different type passed to Decode()?

I have the following data structures which I'd like to parse from an API:
type OrderBook struct {
Pair string `json:"pair"`
UpdateTime int64 `json:"update_time"`
}
type depthResponse struct {
Result OrderBook `json:"result"`
// doesn't matter here
//Cmd string `json:"-"`
}
and when I parse the following:
data := `{"error":{"code":"3016","msg":"交易对错误"},"cmd":"depth"}`
It doesn't fail. Why?
Full source code (playground)
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
type OrderBook struct {
Pair string `json:"pair"`
UpdateTime int64 `json:"update_time"`
}
type depthResponse struct {
Result OrderBook `json:"result"`
}
func main() {
data := `{"error":{"code":"3016","msg":"交易对错误"},"cmd":"depth"}`
r := strings.NewReader(data)
var resp depthResponse
if err := json.NewDecoder(r).Decode(&resp); err != nil {
log.Fatalf("We should end up here: %v", err)
}
fmt.Printf("%+v\n", resp)
}
That's the expected behaviour of Decode (as documented in the Unmarshal function):
https://golang.org/pkg/encoding/json/#Unmarshal
By default, object keys which don't have a corresponding struct field are ignored.
You can however use the DisallowUnknownFields() function (as described in the docs as well) to have it fail if the input JSON has fields not contained in the destination struct.
dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
In that case, you'll get an error as you expect.
Modified playground here: https://play.golang.org/p/A0f6dxTXV34

Adding Arbitrary fields to json output of an unknown struct

In this stackoverflow post it's explained how to add arbitrary fields to a golang struct by using it as an anonymous. This works fine if you are working with known struct types, but I'm wondering how to do the same thing when dealing with an unknown struct or interface.
I wrote the following example to demonstrate:
package main
import (
"os"
"encoding/json"
"fmt"
)
type example interface{}
type Data struct {
Name string
}
func printInterface(val interface{}) {
example1 := struct {
example
Extra string
}{
example: val,
Extra: "text",
}
json.NewEncoder(os.Stdout).Encode(example1)
}
func printStructPointer(val *Data) {
example2 := struct {
*Data
Extra string
}{
Data: val,
Extra: "text",
}
json.NewEncoder(os.Stdout).Encode(example2)
}
func main() {
d := Data{Name:"name"}
fmt.Println("Example 1:")
printInterface(&d)
fmt.Println("Example 2:")
printStructPointer(&d)
}
This prints the following:
Example 1:
{"example":{"Name":"name"},"Extra":"text"}
Example 2:
{"Name":"name","Extra":"text"}
I'm so assuming that I was working within printInterface how do get the JSON output to look like the JSON output of printStructPointer?
There's an important difference between printInterface() and printStructPointer(). The first one embeds an interface type, while the second embeds a struct type (more specifically a pointer to a struct type).
When you embed a struct (or pointer to struct) type, the fields of the embedded type get promoted, so in the 2nd example it will be valid to write example2.Name. When you embed an interface type, an interface does not have fields, so no fields will be promoted. So it doesn't matter if the interface value wraps a struct (or pointer to struct), fields of that struct won't get promoted (they can't be).
Thus, in the printInterface() the interface wrapping a struct won't get "flattened" in the JSON result.
Solving it with generating a dynamic type using reflection
One way to solve this is to generate a dynamic type at runtime, using reflection (reflect package). This new type will be a struct, and it will contain an anonymous struct field being of the type that is wrapped in the passed interface, and will also contain our extra field (of type string).
This is how it could look like:
func printInterface(val interface{}) {
t2 := reflect.StructOf([]reflect.StructField{
reflect.StructField{
Name: "X",
Anonymous: true,
Type: reflect.TypeOf(val),
},
reflect.StructField{
Name: "Extra",
Type: reflect.TypeOf(""),
},
})
v2 := reflect.New(t2).Elem()
v2.Field(0).Set(reflect.ValueOf(val))
v2.FieldByName("Extra").SetString("text")
json.NewEncoder(os.Stdout).Encode(v2.Interface())
}
Output is as expected (try it on the Go Playground):
Example 1:
{"Name":"name","Extra":"text"}
Example 2:
{"Name":"name","Extra":"text"}
Solving it with marshaling twice
Another way would be to marshal the value, unmarshal it into a map, add the extra field and marshal it again:
func printInterface(val interface{}) error {
data, err := json.Marshal(val)
if err != nil {
return err
}
v2 := map[string]interface{}{}
if err := json.Unmarshal(data, &v2); err != nil {
return err
}
v2["Extra"] = "text"
return json.NewEncoder(os.Stdout).Encode(v2)
}
Output is the same. Try it on the Go Playground.
This solution is simpler, easier to follow, but it's slower as it marshals twice. Also note that in this example the fields in the result might be in different order, as iteration order on a map is not specified in Go (for details see Why can't Go iterate maps in insertion order?).
Here's one way:
package main
import (
"encoding/json"
"fmt"
"os"
)
type example interface{}
type Data struct {
Name string
}
func printInterface(val interface{}) {
if d, ok := val.(*Data); ok {
fmt.Println("Asserting type *Data for val is OK")
printStructPointer(d)
} else {
fmt.Println("Asserting type *Data for val is NOT OK")
}
}
func printStructPointer(val *Data) {
example2 := struct {
*Data
Extra string
}{
Data: val,
Extra: "text",
}
json.NewEncoder(os.Stdout).Encode(example2)
}
func main() {
d := Data{Name: "Testing"}
fmt.Println("Example 1:")
printInterface(&d)
fmt.Println("Example 2:")
printStructPointer(&d)
}
Playground: https://play.golang.org/p/OPotpTyUEz
You can also possibly use a type switch for an assertion, esp if you have many types. Hope this helps!