Json unmarshal type model in golang - json

I have a RESTful service that returns response similar to show below:
"Basket" : {
"Count": 1,
"Fruits": {[
{
"Name":"Mango",
"Season":"Summer"
},
{
"Name":"Fig",
"Season":"Winter"}
]}
}
I am trying to create Go lang model to unmarshal the contents. Following is the code I have tried:
type Response struct {
Count int
Fruits []Fruit
}
type Fruit struct {
Name string
Season string
}
But when I marshal the Response object in my test code I don't see similar json. (https://play.golang.org/p/EGKqfbwFvW)
Marshalled data always appears as :
{
"Count":100,
"Fruits":[
{"Name":"Mango","Season":"Summer"},
{"Name":"Fig","Season":"Winter"}
]
}
Notice the Fruits appearing as array [] and not {[]} in original json. How can I model structs in golang for this response?

Your model is totally correct and valid, but the JSON object is not. "Fruits" doesn't have name if it should be key value pair or it should be wrapped in [] not {}.
JSON obj should be formatted like this:
{
"Basket" : {
"Count": 1,
"Fruits": [
{
"Name":"Mango",
"Season":"Summer"
},
{
"Name":"Fig",
"Season":"Winter"
}
]
}
}
And actually invalid json shouldn't work https://play.golang.org/p/yoW7t4NfI7

I would make 'Baskets' a struct within 'Response', create a 'BasketsData' struct, and give it all some labels.
type Fruit struct {
Name string `json:"Name"`
Season string `json:"Season"`
}
type BasketData struct {
Count int `json:"Count"`
Fruits []Fruit `json:"Fruits"`
}
type Response struct {
Basket BasketData `json:"Basket"`
}
This way you will get a top level JSON response when you marshall it.
fruitmania := []Fruit{{Name: "Mango", Season: "Summer"},
{Name: "Fig", Season: "Winter"}}
basket := Response{BasketData{Count: 100, Fruits: fruitmania}}
b, _ := json.Marshal(basket)
fmt.Println(string(b))
checkit-checkit out:
https://play.golang.org/p/TuUwBLs_Ql

Related

Parse a Json file with golang, encountering a problem "cannot unmarshal object into Go value of type"

I have a JSON file as follows:
{
"contracts": [
{
"name": "contract1",
"funcs": [
{
"name": "func1",
"callchain": ["parfunc1", "parfunc2", "parfuncN"],
"stateFuncs": ["stafunc1", "stafunc2", "stafuncN"],
"consOnInputs": ["param1>20", "param2<10", "param4+param5>80"]
},
{
"name": "func2",
"callchain": ["2parfunc1", "2parfunc2", "2parfunN"],
"stateFuncs": ["2stafunc1", "2stafunc2", "2stafuncN"],
"consOnInputs": ["param1>20", "param2<10", "param4+param5>80"]
]
},
{
"name": "contract2",
"funcs": [
{
"name": "func3",
"callchain": ["parfunc5", "parfunc5", "parfuncN"],
"stateFuncs": ["stafunc8", "stafunc8", "stafuncN"],
"consOnInputs": ["param1>20", "param2<10", "param4+param5>80"]
}
]
}
]
}
I encounter the problem json: cannot unmarshal object into Go struct field ContractAll.contracts of type main.ContractInfo
contracts: {[[] []]} with the following unmarshalled codes, where the json_content contains contents of the JSON file:
type FuncInfo struct {
Name string `json:"name"`
Callchain []string `json:"callchain"`
StateFuncs []string `json:"stateFuncs"`
ConsOnInputs []string `json:"consOnInputs"`
}
type ContractInfo []struct{
Name string `json:"name"`
Funcs []FuncInfo `json:"funcs"`
}
type ContractAll struct {
Contracts []ContractInfo `json:"contracts"`
}
var contracts ContractAll
err = json.Unmarshal([]byte(json_content), &contracts)
How can I fix this problem?
Your structures don't match the input. You need one more:
type Contracts struct {
Contracts []ContractInfo `json:"contracts"`
}
...
var contracts Contracts
err = json.Unmarshal([]byte(json_content), &contracts)

How to convert string to JSON in Go?

Let’s say I have this string:
`
{
"testCode": 0,
"replyTest": "OK",
"data": {
"001": {
"fields": {
"name": "arben",
"fav_color": "blue",
"address": "PH",
}
},
"002": {
"fields": {
"name": "john",
"fav_color": "black",
"address": "PH",
}
},
}
}
`
How to convert this string to JSON where data is in form of list in order for me to loop this list in a process?
Whenever you have json property names that aren't known upfront, or they don't lend themselves very well to be represented as fields of a struct then you're left more or less with one option, a map. So first you need to unmarshal the "data" property into a map.
However maps in Go are implemented as unordered groups of elements indexed by a set of unique keys. So basically there's no way to ensure the order of a map in Go, and therefore you'll have to transfer the data from the map into something that can be ordered, like a slice for example.
After you've got your data in the slice you can use the standard sort package to sort the slice in the order you want.
You can start by declaring the types you need:
type DataItem struct {
Key string `json:"-"`
Fields DataFields `json:"fields"`
}
type DataFields struct {
Name string `json:"name"`
FavColor string `json:"fav_color"`
Address string `json:"address"`
}
Then unmarshal the json
var obj struct {
TestCode int `json:"testCode"`
ReplyTest string `json:"replyTest"`
Data map[string]*DataItem `json:"data"`
}
if err := json.Unmarshal(data, &obj); err != nil {
panic(err)
}
Transfer the contents of the map into a slice
items := []*DataItem{}
for key, item := range obj.Data {
item.Key = key // keep track of the key because it will be used to order the contents of the slice
items = append(items, item)
}
Finally sort the slice
sort.Slice(items, func(i, j int) bool {
return items[i].Key < items[j].Key
})
https://play.golang.com/p/D2u46veOQwD

How to show empty object instead of empty struct or nil in Go json marshal

I need to show json's empty object {} when do json.Marshal() for a struct pointer. I can only output either null value or empty struct value.
If the person key is filled with &Person{} or new(Person), it will show empty struct like below:
{
"data": {
"person": {
"name": "",
"age": 0
},
"created_date": "2009-11-10T23:00:00Z"
}
}
And if we don't initialize it at all, it will show null.
{
"data": {
"person": null,
"created_date": "2009-11-10T23:00:00Z"
}
}
I want to show "person": {}. Is it possible?
Go Playground for the complete code: https://play.golang.org/p/tT15G2ESPVc
Option A, use the omitempty tag option on all of the Person's fields and make sure the response's field is allocated before marshaling.
type Person struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
}
// ...
resp.Person = new(Person)
https://play.golang.org/p/o3jWdru_8bC
Option B, use a non-pointer wrapper type that embeds the Person pointer type.
type PersonJSON struct {
*Person
}
type Response struct {
Person PersonJSON `json:"person"`
CreatedDate time.Time `json:"created_date"`
}
https://play.golang.org/p/EKQc7uf1_Vk
Option C, have the Reponse type implement the json.Marshaler interface.
func (r *Response) MarshalJSON() ([]byte, error) {
type tmp Response
resp := (*tmp)(r)
var data struct {
Wrapper struct {
*Person
} `json:"person"`
*tmp
}
data.Wrapper.Person = resp.Person
data.tmp = resp
return json.Marshal(data)
}
https://play.golang.org/p/1qkSCWZ225j
There may be other options...
In Go, an empty struct by definition assigns zero values to field elements. Eg: for int 0, "" for string, etc.
For your case, simply comparing to null would work out. Or, you could define an emptyPerson as:
var BAD_AGE = -1
emptyPerson := &Person{"", BAD_AGE} // BAD_AGE indicates no person
if person[age] == BAD_AGE {
// handle case for emptyPerson}

Unmarshall JSON with a [int]string map contained in an array

Im trying to unmarshall the following JSON into a struct, but there I am unable to translate the contents of the values field with the [[int,string]]
This is what I have so far:
type Response struct {
Metric struct {
Name string `json:"name,omitempty"`
Appname string `json:"appname,omitempty"`
} `json:"metric,omitempty"`
Values []map[int]string `json:"values,omitempty"`
}
The JSON file:
{
"metric":{
"name":"x444",
"appname":"cc-14-471s6"
},
"values":[
[
1508315264,
"0.0012116165566900816"
],
[
1508315274,
"0.0011871631158857396"
]
]
}
The data you showed should be unmarshaled to:
type Response struct {
Metric struct {
Name string `json:"name,omitempty"`
Appname string `json:"appname,omitempty"`
} `json:"metric,omitempty"`
Values [][]interface{} `json:"values,omitempty"`
}
If you want to to transfer it to map implement json.Unmarshaller interface - https://golang.org/pkg/encoding/json/#Unmarshaler
You can have something like:
type Item struct {
Key int
Val string
}
func(item *Item) UnmarshalJSON([]byte) error {
// TODO: implement
}
type Response struct {
Metric struct {
Name string `json:"name,omitempty"`
Appname string `json:"appname,omitempty"`
} `json:"metric,omitempty"`
Values []Item `json:"values,omitempty"`
}

How can I parse JSON in GoLang if nested content uses dynamic keys?

I received JSON below from a client API but i am struggling to get nested JSON content. How can I parse it if the inner keys are dynamic?
const jsonStream = `
{
"items": {
"bvu62fu6dq": {
"name": "john",
"age": 23,
"xyz": "weu33s"
},
"iaxdw23fq": {
"name": "kelly",
"age": 21,
"xyz": "weu33s"
}
}
}`
This is what i have tried below by looping the map to extract the value of name and age from above JSON string; but it returns map with nil value as a result.
goplaygound
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
type Item struct {
Contact struct {
Info map[string]Person
} `json:"items"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var item Item
if err := dec.Decode(&item); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", item.Contact.Info["bvu62fu6dq"].Name)
}
}
Try this instead, looks like you just have your structure set up incorrectly:
http://play.golang.org/p/VRKbv-GVQB
You need to parse the entire json string, which is an object that contains a single element named items. items then contains a map of string -> Person objects.
If you only want to extract name and age from each person, you do it by grabbing data.Items["bvu62fu6dq"].Name.
If you want dynamic keys inside the Person, you'll need to do map[string]interface{} instead of Person in order to capture the dynamic keys again. It would look something like:
type Data struct {
Items map[string]map[string]interface{} `json:"items"`
}
...
fmt.Printf("%v\n", data.Items["bvu62fu6dq"]["name"]
fmt.Printf("%v\n", data.Items["bvu62fu6dq"]["age"]
fmt.Printf("%v\n", data.Items["bvu62fu6dq"]["xyz"]