JSON to struct conversion - json

This is the struct I have:
type Resource struct {
Name string `json:"name"`
Ranges struct {
Range []struct {
Begin int `json:"begin"`
End int `json:"end"`
} `json:"range"`
} `json:"ranges,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type"`
Scalar Scalar `json:"scalar,omitempty"`
}
I don't know how to make fields in the JSON not null. For example, struct Range like that:
{
"name": "cpus",
"ranges": {
"range": null
},
"type": "SCALAR",
"scalar": {
"value": 1
}
}, {
"name": "mem",
"ranges": {
"range": null
}

a way to do that is assign range as *string and then you should compare it with nil or not, if not nil convert it to string and marshall it again

That changes struct is resolved my problem:
type Resource struct {
Name string `json:"name"`
Ranges *Ranges `json:"ranges,omitempty"`
Role string `json:"role,omitempty"`
Type string `json:"type"`
Scalar *Scalar `json:"scalar,omitempty"`
}

assuming that you want to marshal the structs in your question and get a json output that looks like this:
{
"name": "cpus",
"ranges": {
"range": []
},
"type": "SCALAR",
"scalar": {
"value": 1
}
},
{
"name": "mem",
"ranges": {
"range": []
}
}
In golang slices [] are a reference type, that are backed by an array.
You can read up on the internals of slices here: https://blog.golang.org/go-slices-usage-and-internals
Basically the reason that you are getting null in the output is because you have not instantiated the slice, the slice is essentially a pointer, and that pointer is nil.
Create a new empty slice, like []Range{} and assign that to the field in Resource that is currently null in the json, and instead of a nil pointer, you will have an empty slice that will be marshalled as [] and not null.

Related

Trouble mapping json to golang struct

I have a json stream as follows ...
[
{
"page": 1,
"pages": 7,
"per_page": "2000",
"total": 13200
},
[
{
"indicator": {
"id": "SP.POP.TOTL",
"value": "Population, total"
},
"country": {
"id": "1A",
"value": "Arab World"
},
"value": null,
"decimal": "0",
"date": "2019"
},
{
"indicator": {
"id": "SP.POP.TOTL",
"value": "Population, total"
},
"country": {
"id": "1A",
"value": "Arab World"
},
"value": "419790588",
"decimal": "0",
"date": "2018"
},
...
]
]
And I'm trying to decode it ... so I have the following struct ... but I keep getting
"cannot unmarshal array into Go value of type struct { P struct ... "
type Message []struct {
P struct {
Page int
}
V []struct {
Indicator struct {
Id string
Value string
}
Country struct {
Value string
}
Value string
Decimal string
Date string
}
}
My struct looks to match the json ... but obviously not! Any ideas?
Since your JSON array have two different types first unmarshal them into a slice of json.RawMessage which is []byte as underlying type so that we can unmarshal again JSON array data.
So unmarshal data for P and V struct type using index directly (predict) or detect if object(starting with '{') then unmarshal into P and array(starting with '[') then unmarshal into V. Now prepare your Message using those data.
type Message struct {
PageData P
ValData []V
}
type P struct {
Page int
}
type V struct {
Indicator struct {
Id string
Value string
}
Country struct {
Value string
}
Value string
Decimal string
Date string
}
func main() {
var rawdata []json.RawMessage
json.Unmarshal([]byte(jsonData), &rawdata)
var pageData P
json.Unmarshal(rawdata[0], &pageData)
var valData []V
json.Unmarshal(rawdata[1], &valData)
res := Message{pageData, valData}
fmt.Println(res)
}
var jsonData = `[...]` //your json data
Full code in Go Playground
As poWar said, the JSON you actually have is a list of objects whose types do not conform to each other. You must therefore unmarshal into something capable of holding different object types, such as interface{} or—since there is an outer array—[]interface{}.
You can also, if you like, decode into a []json.RawMessage. The underlying json.RawMessage itself has underlying type []byte so that it's basically the undecoded "inner" JSON. In at least some cases this is going to be more work than just decoding directly to []interface{} and checking each resulting interface, but you can, if you wish, decode to struct once you have the JSON separated out. For instance:
func main() {
var x []json.RawMessage
err := json.Unmarshal(input, &x)
if err != nil {
fmt.Printf("err = %v\n", err)
return
}
if len(x) != 2 {
fmt.Println("unexpected input")
return
}
var page struct {
Page int
}
err = json.Unmarshal(x[0], &page)
if err != nil {
fmt.Printf("unable to unmarshal page part: %v\n", err)
return
}
fmt.Printf("page = %d\n", page.Page)
// ...
}
Here on the Go Playground is a more complete example. See also Eklavya's answer.
Looking at your struct, your corresponding JSON should look something like this.
[
{
"P": {"page": 1},
"V": [
{
"Indicator": {"Id": ...},
"Country": {"Value":""},
"Value": "",
...
}
]
},
...
]
The JSON structure you are trying to Unmarshal looks like a list of objects where each object is not of the same type. You can start unmarshalling them into interfaces and defining each interface based on the object being unmarhsalled.
package main
import (
"encoding/json"
"log"
)
type Message []interface{}
func main() {
data := `[{"page":1,"pages":7,"per_page":"2000","total":13200},[{"indicator":{"id":"SP.POP.TOTL","value":"Population, total"},"country":{"id":"1A","value":"Arab World"},"value":null,"decimal":"0","date":"2019"},{"indicator":{"id":"SP.POP.TOTL","value":"Population, total"},"country":{"id":"1A","value":"Arab World"},"value":"419790588","decimal":"0","date":"2018"}]]`
var m Message
if err := json.Unmarshal([]byte(data), &m); err != nil {
log.Fatalf("could not unmarshal")
}
log.Printf("message: %v", m)
}
Output:
message: [map[page:1 pages:7 per_page:2000 total:13200] [map[country:map[id:1A value:Arab World] date:2019 decimal:0 indicator:map[id:SP.POP.TOTL value:Population, total] value:<nil>] map[country:map[id:1A value:Arab World] date:2018 decimal:0 indicator:map[id:SP.POP.TOTL value:Population, total] value:419790588]]]
[Edit]: Ideally you should change your JSON to be structured better for unmarshalling. If you do not have control on it, then your corresponding Go structure is just embedded maps of string to interfaces, which you will have to manually type cast and access.

How to create a infinite nested json and access the same in go lang data structures?

How to create a infinite nested json and access the same in go lang data structures,
For example below is the sample json with 3 levels, in general it should be dynamic, user can add any children by selecting the value from the dropdown which is populated tree like dropdown on UI.
{
"value": {
"Id": "1",
"Text": "abcd",
"Parent": ""
},
"children": [
{
"value": {
"Id": "2",
"Text": "abcd",
"Parent": "1"
},
"children": [
{
"value": {
"Id": "3",
"Text": "abcd",
"Parent": "1"
}
}
]
}
]
}
structures in go: I have created this go data structure but it will access only upto 3 levels based on the above json, can we make this data structure as dynamic where it should handle infinite nested json.
type AutoGenerated struct {
Value struct {
ID string `json:"Id"`
Text string `json:"Text"`
Parent string `json:"Parent"`
} `json:"value"`
Children []struct {
Value struct {
ID string `json:"Id"`
Text string `json:"Text"`
Parent string `json:"Parent"`
} `json:"value"`
Children []struct {
Value struct {
ID string `json:"Id"`
Text string `json:"Text"`
Parent string `json:"Parent"`
} `json:"value"`
} `json:"children"`
} `json:"children"`
}
Note: we can add n number of parents and n number of children, is this possible in Go data structures?
can you suggest best and easy way to implement this?
How can we add/delete/edit any parent or child? (The above json example will come from UI) ? To add/delete/edit any parent or child which json structure or id needed?
You can use recursive structures in Go to represent this json (by recursive, I mean that Level contains a []Level):
type Value struct {
ID string
Text string
Parent string
}
type Level struct {
Value Value `json:"value"`
Children []Level
}
Given the json you listed as the string j, I can now unmarshal it as follows:
var root Level
err := json.Unmarshal([]byte(j), &root)
if err != nil {
panic(err)
}
fmt.Println(root)
fmt.Println(root.Children[0])
fmt.Println(root.Children[0].Children[0])
This outputs:
{{1 abcd } [{{2 abcd 1} [{{3 abcd 1} []}]}]}
{{2 abcd 1} [{{3 abcd 1} []}]}
{{3 abcd 1} []}
Go playground link
Addition to #marc, you can combine these two struct Value & Level in a single structure. Like AutoGenerated one, you only need to use [ ]Level as Children.
type Level struct {
Value struct {
ID string
Text string
Parent string
} `json:"value"`
Children []Level
}
See in playground
Based on #Marc's data structure, a simple add:
func (l *Level) Add(path,Id,Text string) error {
if path=="" {
l.Children = append(l.Children, Level{ Value: Value{Id,Text,l.Value.ID} })
return nil
}
x:=strings.SplitN(path,".",2)
name,remain:=x[0],""
if len(x)>1 {
remain = x[1]
}
for i:=range l.Children {
if l.Children[i].Value.ID == name {
return l.Children[i].Add(remain,Id,Text)
}
}
return errors.New("Not found")
}
func main() {
var root Level
err := json.Unmarshal([]byte(j), &root)
if err != nil {
panic(err)
}
fmt.Println(root)
fmt.Println(root.Children[0])
fmt.Println(root.Children[0].Children[0])
fmt.Println(root.Add("2.3","4","xxx"))
fmt.Println(root)
}
Remove and modify would be mostly the same.
Playground: https://play.golang.org/p/7sWRJ-9EQSv

MGO return bson field instead of json field

The bson name is used when performing pipe in mgo.
Struct :
type Training struct {
Id bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Description string `json:"description"`
Level *TrainingLevel `json:"level"`
Preworks []bson.ObjectId `json:"preworks"`
PrePostTests []bson.ObjectId `json:"preposttests" bson:"preposttests"`
TrainingEvaluations []bson.ObjectId `json:"training_evaluations" bson:"training_evaluations"`
TrainerEvaluations []bson.ObjectId `json:"trainer_evaluations" bson:"trainer_evaluations"`
AppCompanyId bson.ObjectId `json:"app_company_id" bson:"app_company_id"`
Company *Company `json:"company"`
}
Function :
func (this *TrainingClass) GetAllTraining() (interface{}, error) {
if !this.tokenInfo.IsAllowed(this.c) {
return nil, tlib.NewTError(common.Error_NoAccess, "You don't have the right!")
}
sess, db := GetDB()
defer sess.Close()
pipeline := []bson.M{
{"$match": bson.M{
"app_company_id": this.tokenInfo.AppCompanyId}},
{"$lookup": bson.M{
"from": "trainingbatch",
"localField": "_id",
"foreignField": "training._id",
"as": "trainingbatches"}},
}
resp := []bson.M{}
db.C(common.C_TRAINING).Pipe(pipeline).All(&resp)
return bson.M{"data": resp}, nil
}
Json result :
{
"data": [
{
"_id": "5995a749dbcfbe4e8cc31378",
"app_company_id": "58b24756e65bd121f6b1a923",
"description": "Description First Training",
"name": "First Training",
"trainingbatches": [
{
"_id": "5995a74adbcfbe4e8cc31379",
"app_company_id": "58b24756e65bd121f6b1a923",
"company": {
"_id": "58b24756e65bd121f6b1a923",
"address": "",
"app_company_id": "58b24756e65bd121f6b1a923",
"fullname": "",
"name": "Tandem",
"phone": ""
},
}
]
}
]
}
As you can see field _id is generated instead of id. That's not happen if I use find or findId. Is there any way to keep using json field no matter what's the query?
The way you're reading the result, it has no idea what the JSON field names are. In order for it to use those tags, it must actually deserialize into the struct where the tags have been specified. When you do:
resp := []bson.M{}
db.C(common.C_TRAINING).Pipe(pipeline).All(&resp)
You're explicitly telling mgo to return BSON results. The object you pass in (a slice of bson.M) has no json tags on it. In order to control the serialization to JSON, you must pass a struct with the JSON tags specified to All:
resp := []Training
db.C(common.C_TRAINING).Pipe(pipeline).All(&resp)

How do I parse a nested array in a nested JSON object in Golang?

I have a JSON:
{
"data": [
{
"id": 1,
"values": [
[
{
"id": "11",
"keys": [
{
"id": "111"
}
]
}
]
]
}
]
}
I want to parse "values" and "keys" into structs, but I don't known what type should i use in "Data"?:
type Value struct {
Id string `json:"id"`
Keys []Key `json:"keys"`
}
type Key struct {
Id string `json:"id"`
}
type Result struct {
Data []Data `json:"data"`
}
type Data struct {
Id int `json:"id"`
Values []???? `json:"values"`
}
I would be grateful for any help. Thanks.
If you look carefully at your json. you have an array in an array...
...
"values": [
[...
If this is intended then the type of values is:
[][]Value
to represent the two arrays, else remove the array nesting and it becomes:
[]Value
Runnable Example: https://play.golang.org/p/UUqQR1KSwB
type Basic struct {
ID string `json:"id"`
}
type Inner struct {
ID string `json:"id"`
Keys []Basic `json:"keys"`
}
type Middle struct {
ID int `json:"id"`
Values []Inner `json:"values"`
}
type Final struct {
Data []Middle `json:"data"`
}

Unable to decode the JSON response

I have the following response from a graph api
{
"data": [
{
"name": "Mohamed Galib",
"id": "502008940"
},
{
"name": "Mebin Joseph",
"id": "503453614"
},
{
"name": "Rohith Raveendranath",
"id": "507482441"
}
],
"paging": {
"next": "https://some_url"
}
}
I have a struct as follows
type Item struct {
Name, Id string
}
I wanted to parse the response and get an array of Item, How do I do that?
You need to update your struct like so:
type Item struct {
Name string `json:"name"`
Id string `json:"id"`
}
and add a struct to represent the wrapper:
type Data struct {
Data []Item `json:"data"`
}
You can then use json.Unmarshal to populate a Data instance.
See the example in the docs.