JSON: Nesting a populated struct into a new struct - json

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.

Related

Golang json unmarshall

I'm new in Go. I have json like this:
{
"3415": {
"age": 25,
"name": "Tommy"
},
"3414": {
"age": 21,
"name": "Billy"
}
}
I want to unmarshall it to struct:
type People struct {
Id map[string]PeopleDetails
}
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}
But while I run it, I see that struct return nil value.
I did read some tutorials, but most of them have predefined keys, as You see here "id" e.g. 3415 is different for every new json.
When you have to deal with a "dynamic" json key, the answer is use a map of struct.
You can use the following code:
package main
import (
"encoding/json"
"fmt"
)
// Use the struct pointed by #Adirio
type People map[string]PeopleDetails
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}
var data string = `{"3415":{"age":25,"name":"Tommy"},"3414":{"age":21,"name":"Billy"}}`
func main() {
var p People
if err := json.Unmarshal([]byte(data), &p); err != nil {
fmt.Println(err)
}
fmt.Println(p)
}
GoPlayground: https://play.golang.org/p/kVzNV56NcTd
Try with these types instead:
type People map[string]PeopleDetails
type PeopleDetails struct {
Age int `json:"age"`
Name string `json:"name"`
}

How to show json at same level for interface nested inside struct?

Here has an interface SpecificTemplate nested inside struct Template:
package main
import (
"encoding/json"
"fmt"
)
type SpecificTemplate interface {
GetLabel() string
}
type TemplateA struct {
Label string `json:"label"`
}
func (t TemplateA) GetLabel() string {
return t.Label
}
type Template struct {
Id int64 `json:"id"`
SpecificTemplate
}
func main() {
ta := TemplateA{
Label: `label1`,
}
t := Template{
Id: 1,
SpecificTemplate: ta,
}
data, _ := json.Marshal(t)
fmt.Println(string(data))
}
It would be
{
"id":1,
"SpecificTemplate":{
"label":"label1"
}
}
I wanna kown how to show json at same level, just like:
{
"id":1,
"label":"label1"
}
It kinda depends on the level of complexity you wanna reach...
If you want to expose only the label I should advice you to create a MarshalJSON function, just like this...
func (t Template) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Id int64 `json:"id"`
Label string `json:"label"`
}{
Id: t.Id,
Label: t.SpecificTemplate.GetLabel(),
})
}
With this your json.Marshal(t) will call this function and you will receive a flattened json...
However if you want to expose more Fields from the template you should use reflection, as pointed out by Iain Duncan in his comment

How to unmarshal JSON array of different datatypes?

Part of the JSON I'm trying to unmarshal has an array that can contain either strings or ints. How would I go about parsing this?
{
"id": "abc",
"values": [1,2,3]
},
{
"id": "def",
"values": ["elephant", "tomato", "arrow"]
},
{
//etc...
}
I tried the following:
type Thing struct {
ID string `json:"id"`
Values []string `json:"values,string,omitempty"`
}
Get the following error:
panic: json: cannot unmarshal array into Go struct field Thing.values of type string
You can set the type of the field Values to []interface{}, but you might have to use some type assertions.
Here is a link to a playground where I use the data you provided: https://play.golang.org/p/ahePgggr8o1
I will paste the code here anyways:
package main
import (
"fmt"
"encoding/json"
)
type Thing struct {
ID string `json:"id"`
Values []interface{} `json:"values,omitempty"`
}
func print(values []interface{}) {
for _, v := range values{
switch value := v.(type){
case string:
fmt.Println("String", value)
case int:
fmt.Println("Integer", value)
case float64:
fmt.Println("Floats", value)
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
}
func main() {
var t1, t2 Thing
data := []byte("{ \"id\": \"abc\", \"values\": [1,2,3]}")
data2 := []byte("{ \"id\": \"def\", \"values\": [\"elephant\", \"tomato\", \"arrow\"]}")
_ = json.Unmarshal(data, &t1)
_ = json.Unmarshal(data2, &t2)
print(t1.Values)
print(t2.Values)
}

Golang return lower case json key

I send Json data with net/http package by an Url, i want to have some lowercase keys in return, but it's not working.
In this example of the problem, i want lowercase 'count' and 'data' key.
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type tableau struct {
Count int `json"count"`
Data []People `json"data"`
}
type People struct {
Id int `json"Id"`
Name string `json"Name"`
Age int `json"Age"`
}
func main() {
http.HandleFunc("/people", recupPeople)
fs := http.FileServer(http.Dir("Static"))
http.Handle("/", fs)
http.ListenAndServe(":80", nil)
}
func recupPeople(w http.ResponseWriter, r *http.Request) {
listPeople := &tableau{
Count: 4,
Data: []People{
People{Id: 1, Name: "Laurent", Age: 20},
People{Id: 2, Name: "Laurent", Age: 20},
},
}
peop, _ := json.Marshal(listPeople)
fmt.Println(string(peop))
w.Write(peop)
json.NewEncoder(w).Encode(listPeople)
}
But when i check the URL i didn't have lower case.
Cordially,
Laurent
You forgot colon in tag declaration. As tags are not in proper format, field names are in your json.
Try this:
type tableau struct {
Count int `json:"count"`
Data []People `json:"data"`
}
Try adding a : to your struct tags:
type tableau struct {
Count int `json:"count"`
Data []People `json:"data"`
}

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)
}