Decoding a slice of maps from a JSON string in Golang - json

Following the Go by Example: JSON tutorial, I see how to work with a basic JSON string:
package main
import (
"encoding/json"
"fmt"
)
type Response struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
func main() {
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := Response{}
json.Unmarshal([]byte(str), &res)
fmt.Println(res.Page)
fmt.Println(res.Fruits)
}
// the output looks good here:
// 1
// [apple peach]
I would like to add some complexity to the str data object that I am decoding.
Namely, I would like to add a key with a slice of maps as its value:
"activities": [{"name": "running"}, {"name", "swimming"}]
My script now looks like below example, however, for the life of me, I can not figure out what the correct syntax is in the Response struct in order to get at the values in activities. I know that this syntax isn't correct: Activities []string ... but can not hack my way towards a solution that captures the data I want to display.
package main
import (
"encoding/json"
"fmt"
)
type Response struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
Activities []string `json:"activities"`
}
func main() {
str := `{"page": 1, "fruits": ["apple", "peach"], "activities": [{"name": "running"}, {"name", "swimming"}]}`
res := Response{}
json.Unmarshal([]byte(str), &res)
fmt.Println(res.Page)
fmt.Println(res.Fruits)
fmt.Println(res.Activities)
}
// the script basically craps out here and returns:
// 0
// []
// []
Thanks for any help!

Use []map[string]string:
type Response struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
Activities []map[string]string `json:"activities"`
}
playground example
Always check and handle errors.
The example JSON has a syntax error which is corrected in the playground example.

I know this is an old one, but i've recently written utility for generating exact go type from json input and in similar case you could give it a spin: https://github.com/m-zajac/json2go
For this particular example it generates from json:
{"page": 1, "fruits": ["apple", "peach"], "activities": [{"name": "running"}, {"name": "swimming"}]}
go type:
type Object struct {
Activities []struct {
Name string `json:"name"`
} `json:"activities"`
Fruits []string `json:"fruits"`
Page int `json:"page"`
}

Related

Converting JSON that contains only 1 field of arrays

I'm trying to convert a JSON that contains only 1 field which apparently an array to a complex struct in Golang but unfortunately I'm not getting the data back, instead, I got:
{Result:[]}
Anyone knows why? (code below)
package main
import (
"encoding/json"
"fmt"
)
type Account struct {
AccountId string
}
type Response struct {
Result []Account
}
func main() {
input := []byte(`{
"result": [
{"account_id" : "1"},
{"account_id" : "2"},
{"account_id" : "3"},
]
}
`)
var resp Response
json.Unmarshal(input, &resp)
fmt.Printf("%+v\n", resp)
}
use a explicit tag in your stucture type.
type Account struct {
AccountId string `json:"account_id, omitempty"`
}
If you are a novice, keep in mind the JSON size, if is large then use a stream library (jstream or easyjson etc),
other advice is check nullables or omit when they are empty anyway you can use nullable library like https://github.com/guregu/null
Cheers!

JSON: Nesting a populated struct into a new struct

I have a struct like so:
type my_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
When I marshal that into JSON, it outputs very simple JSON as you'd expect:
var output_json []byte
output_json, _ = json.Marshal(output)
fmt.Println(string(output_json))
Result:
{"first":"my_string","second":"another_string","number":2}
All fine so far!
What I'd like to do, before marshalling that struct into JSON, is nest it inside another struct. The resulting output would be JSON that looks like this:
{
"fields": {
"first": "my_string",
"number": 2,
"second": "another_string"
},
"meta": "data"
}
How can I do that?
I think you could statically declare another struct to use:
type WrappedOutput struct {
fields my_struct
meta string
}
Then you could embed your struct before marshalling
o := WrappedOutput{fields: output}
Brand new to go so not sure if this is the easiest way to do it
The clean way to do this would be to declare 2 structs (I've done it globally below) and nest My_struct inside the higher level struct.
Then you can initialize the higher level struct before Mashalling it:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
type Big_struct struct {
Fields My_struct
Meta string
}
func main() {
output := Big_struct{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
if you don't want Big_struct you can declare an anonymous struct within your code as you need it and nest My_struct inside:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json: "second"`
Number int `json:"number"`
}
func main() {
output := struct {
Fields My_struct
Meta string
}{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
If you don't want to use a new structure, you can do:
data := my_struct{First: "first", Second: "2", Number: 123}
result, _ := json.Marshal(&map[string]interface{}{"fields":data,"meta":"data"})
fmt.Println(string(result))
it's not clean, but it does the work.

Why struct fields are showing empty?

I am struggling to get the correct output from the following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var jsonBlob3 = []byte(`[
{"name": "Platypus", "spec": "Monotremata", "id":25 },
{"name": "Quoll", "spec": "Dasyuromorphia", "id":25 }
]`)
type Animal2 struct {
name string
spec string
id uint32
}
var animals []Animal2
err := json.Unmarshal(jsonBlob3, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v\n", animals)
}
Playground snippet
The struct fields are empty when printed. I am sure there is a dumb mistake somewhere but I am still new to Go and I have been stuck at this for hours. Please help.
This has come up so many times. The problem is that only exported fields can be marshaled/unmarshaled.
Export the struct fields by starting them with capital (upper-case) letters.
type Animal2 struct {
Name string
Spec string
Id uint32
}
Try it on the Go Playground.
Note that the JSON text contains the field names with lowercased text, but the json package is "clever" enough to match them. If they would be completely different, you could use struct tags to tell the json package how they are found (or how they should be marshaled) in the JSON text, e.g.:
type Animal2 struct {
Name string `json:"json_name"`
Spec string `json:"specification"`
Id uint32 `json:"some_custom_id"`
}

JSON unmarshaling with long numbers gives floating point number

I was marshaling and unmarshaling JSONs using golang and when I want to do it with number fields golang transforms it in floating point numbers instead of use long numbers, for example.
I have the following JSON:
{
"id": 12423434,
"Name": "Fernando"
}
After marshal it to a map and unmarshal again to a json string I get:
{
"id":1.2423434e+07,
"Name":"Fernando"
}
As you can see the "id" field is in floating point notation.
The code that I am using is the following:
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
//Create the Json string
var b = []byte(`
{
"id": 12423434,
"Name": "Fernando"
}
`)
//Marshal the json to a map
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
//print the map
fmt.Println(m)
//unmarshal the map to json
result,_:= json.Marshal(m)
//print the json
os.Stdout.Write(result)
}
It prints:
map[id:1.2423434e+07 Name:Fernando]
{"Name":"Fernando","id":1.2423434e+07}
It appears to be that the first marshal to the map generates the FP. How can I fix it to a long?
This is the link to the program in the goland playground:
http://play.golang.org/p/RRJ6uU4Uw-
There are times when you cannot define a struct in advance but still require numbers to pass through the marshal-unmarshal process unchanged.
In that case you can use the UseNumber method on json.Decoder, which causes all numbers to unmarshal as json.Number (which is just the original string representation of the number). This can also useful for storing very big integers in JSON.
For example:
package main
import (
"strings"
"encoding/json"
"fmt"
"log"
)
var data = `{
"id": 12423434,
"Name": "Fernando"
}`
func main() {
d := json.NewDecoder(strings.NewReader(data))
d.UseNumber()
var x interface{}
if err := d.Decode(&x); err != nil {
log.Fatal(err)
}
fmt.Printf("decoded to %#v\n", x)
result, err := json.Marshal(x)
if err != nil {
log.Fatal(err)
}
fmt.Printf("encoded to %s\n", result)
}
Result:
decoded to map[string]interface {}{"id":"12423434", "Name":"Fernando"}
encoded to {"Name":"Fernando","id":12423434}
The JSON standard doesn't have longs or floats, it only has numbers. The json package will assume float64 when you haven't defined anything else (meaning, only provided Unmarshal with an interface{}).
What you should do is to create a proper struct (as Volker mentioned):
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
func main() {
//Create the Json string
var b = []byte(`{"id": 12423434, "Name": "Fernando"}`)
//Marshal the json to a proper struct
var f Person
json.Unmarshal(b, &f)
//print the person
fmt.Println(f)
//unmarshal the struct to json
result, _ := json.Marshal(f)
//print the json
os.Stdout.Write(result)
}
Result:
{12423434 Fernando}
{"id":12423434,"name":"Fernando"}
Playground: http://play.golang.org/p/2R76DYVgMK
Edit:
In case you have a dynamic json structure and wish to use the benefits of a struct, you can solve it using json.RawMessage. A variable of type json.RawMessage will store the raw JSON string so that you later on, when you know what kind of object it contains, can unmarshal it into the proper struct. No matter what solution you use, you will in any case need some if or switch statement where you determine what type of structure it is.
It is also useful when parts of the JSON data will only be copied to the another JSON object such as with the id-value of a JSON RPC request.
Example of container struct using json.RawMessage and the corresponding JSON data:
type Container struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
var b = []byte(`{"type": "person", "data":{"id": 12423434, "Name": "Fernando"}}`)
A modified version of your example on Playground: http://play.golang.org/p/85s130Sthu
Edit2:
If the structure of your JSON value is based on the name of a name/value pair, you can do the same with a:
type Container map[string]json.RawMessage

How to parse an inner field in a nested JSON object

I have a JSON object similar to this one:
{
"name": "Cain",
"parents": {
"mother" : "Eve",
"father" : "Adam"
}
}
Now I want to parse "name" and "mother" into this struct:
struct {
Name String
Mother String `json:"???"`
}
I want to specify the JSON field name with the json:... struct tag, however I don't know what to use as tag, because it is not the top object I am interested in. I found nothing about this in the encoding/json package docs nor in the popular blog post JSON and Go. I also tested mother, parents/mother and parents.mother.
You could use structs so long as your incoming data isn't too dynamic.
http://play.golang.org/p/bUZ8l6WgvL
package main
import (
"fmt"
"encoding/json"
)
type User struct {
Name string
Parents struct {
Mother string
Father string
}
}
func main() {
encoded := `{
"name": "Cain",
"parents": {
"mother": "Eve",
"father": "Adam"
}
}`
// Decode the json object
u := &User{}
err := json.Unmarshal([]byte(encoded), &u)
if err != nil {
panic(err)
}
// Print out mother and father
fmt.Printf("Mother: %s\n", u.Parents.Mother)
fmt.Printf("Father: %s\n", u.Parents.Father)
}
Unfortunately, unlike encoding/xml, the json package doesn't provide a way to access nested values. You'll want to either create a separate Parents struct or assign the type to be map[string]string. For example:
type Person struct {
Name string
Parents map[string]string
}
You could then provide a getter for mother as so:
func (p *Person) Mother() string {
return p.Parents["mother"]
}
This may or may not play into your current codebase and if refactoring the Mother field to a method call is not on the menu, then you may want to create a separate method for decoding and conforming to your current struct.
Here's some code I baked up real quick in the Go Playground
http://play.golang.org/p/PiWwpUbBqt
package main
import (
"fmt"
"encoding/json"
)
func main() {
encoded := `{
"name": "Cain",
"parents": {
"mother": "Eve"
"father": "Adam"
}
}`
// Decode the json object
var j map[string]interface{}
err := json.Unmarshal([]byte(encoded), &j)
if err != nil {
panic(err)
}
// pull out the parents object
parents := j["parents"].(map[string]interface{})
// Print out mother and father
fmt.Printf("Mother: %s\n", parents["mother"].(string))
fmt.Printf("Father: %s\n", parents["father"].(string))
}
There might be a better way. I'm looking forward to seeing the other answers. :-)
More recently, gjson supports selection of nested JSON properties.
name := gjson.Get(json, "name")
mother := gjson.Get(json, "parents.mother")
How about using an intermediary struct as the one suggested above for parsing, and then putting the relevant values in your "real" struct?
import (
"fmt"
"encoding/json"
)
type MyObject struct{
Name string
Mother string
}
type MyParseObj struct{
Name string
Parents struct {
Mother string
Father string
}
}
func main() {
encoded := `{
"name": "Cain",
"parents": {
"mother": "Eve",
"father": "Adam"
}
}`
pj := &MyParseObj{}
if err := json.Unmarshal([]byte(encoded), pj); err != nil {
return
}
final := &MyObject{Name: pj.Name, Mother: pj.Parents.Mother}
fmt.Println(final)
}