How to create JSON for Go struct - json

I am trying to use the Marshal function to create JSON from a Go struct. The JSON created does not contain the Person struct.
What am I missing?
http://play.golang.org/p/ASVYwDM7Fz
type Person struct {
fn string
ln string
}
type ColorGroup struct {
ID int
Name string
Colors []string
P Person
}
per := Person{
fn: "John",
ln: "Doe",
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
P: per,
}
b, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
The output generated is as follows:
{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"],"P":{}}
I don't see Person in the output.
http://golang.org/pkg/encoding/json/#Marshal

You are missing two things.
Only Public fields can be Marshaled to json.
The name written to json is the name of the fieldd. In this case P for the field Person.
Notice that I changed the Fields name to be capital for the Person struct and that I added
a tag json on the ColorGroup Struct to indicate that I want that field to be serialized with another name. Is common to tag most of the fields and change the name to lowercase to be in sync with javascript's style.
http://play.golang.org/p/HQQ8r8iV7l
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
type Person struct {
Fn string
Ln string
}
type ColorGroup struct {
ID int
Name string
Colors []string
P Person `json:"Person"`
}
per := Person{Fn: "John",
Ln: "Doe",
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
P: per,
}
b, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
}
Will output
{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"],"Person":{"Fn":"John","Ln":"Doe"}}

Related

Unmarshalling of JSON with dynamic keys

I have a scenario where the JSON that has dynamic set of fields that need to get unmarshalled in to a struct.
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
type Status struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status map[string]Status `json:"status"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(person)
fmt.Println(person.Status["bvu62fu6dq"])
}
}
The output:
{john 23 map[]}
{false}
When it gets unmarshalled, the nested status struct is not being correctly resolved to the value in the JSON (shows false even with true value in JSON), is there any issue in the code?
Your types don't really match with the JSON you have:
type Status struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status map[string]Status `json:"status"`
}
Maps to JSON that looks something like this:
{
"name": "foo",
"age": 12,
"status": {
"some-string": {
"Status": true
}
}
}
The easiest way to unmarshal data with a mix of known/unknown fields in a go type is to have something like this:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"` // skip this key
}
Then, first unmarshal the known data:
var p Person
if err := json.Unmarshal([]byte(jsonStream), &p); err != nil {
panic(err)
}
// then unmarshal the rest of the data
if err := json.Unmarshal([]byte(jsonStream), &p.Random); err != nil {
panic(err)
}
Now the Random map will contain every and all data, including the name and age fields. Seeing as you've got those tagged on the struct, these keys are known, so you can easily delete them from the map:
delete(p.Random, "name")
delete(p.Random, "age")
Now p.Random will contain all the unknown keys and their respective values. These values apparently will be an object with a field status, which is expected to be a boolean. You can set about using type assertions and convert them all over to a more sensible type, or you can take a shortcut and marshal/unmarshal the values. Update your Person type like so:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
Statuses map[string]Status `json:"-"`
}
Now take the clean Random value, marshal it and unmarshal it back into the Statuses field:
b, err := json.Marshal(p.Random)
if err != nil {
panic(err)
}
if err := json.Unmarshal(b, &p.Statuses); err != nil {
panic(err)
}
// remove Random map
p.Random = nil
The result is Person.Statuses["bvu62fu6dq"].Status is set to true
Demo
Cleaning this all up, and marshalling the data back
Now because our Random and Statuses fields are tagged to be ignored for JSON marshalling (json:"-"), marshalling this Person type won't play nice when you want to output the original JSON from these types. It's best to wrap this logic up in a custom JSON (un)-Marshaller interface. You can either use some intermediary types in your MarshalJSON and UnmarshalJSON methods on the Person type, or just create a map and set the keys you need:
func (p Person) MarshalJSON() ([]byte, error) {
data := make(map[string]interface{}, len(p.Statuses) + 2) // 2 being the extra fields
// copy status fields
for k, v := range p.Statuses {
data[k] = v
}
// add known keys
data["name"] = p.Name
data["age"] = p.Age
return json.Marshal(data) // return the marshalled map
}
Similarly, you can do the same thing for UnmarshalJSON, but you'll need to create a version of the Person type that doesn't have the custom handling:
type intermediaryPerson struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
}
// no need for the tags and helper fields anymore
type Person struct {
Name string
Age int
Statuses map[string]Status // Status type doesn't change
}
func (p *Person) UnmarshalJSON(data []byte) error {
i := intermediaryPerson{}
if err := json.Unmarshal(data, &i); err != nil {
return err
}
if err := json.Unmarshal(data, &i.Random); err != nil {
return err
}
delete(i.Random, "name")
delete(i.Random, "age")
stat, err := json.Marshal(i.Random)
if err != nil {
return err
}
// copy known fields
p.Name = i.Name
p.Age = i.Age
return json.Unmarshal(stat, &p.Statuses) // set status fields
}
In cases like this, it's common to create a type that handles the known fields and embed that, though:
type BasePerson struct {
Name string `json:"name"`
Age int `json:"age"`
}
and embed that in both the intermediary and the "main"/exported type:
type interPerson struct {
BasePerson
Random map[string]interface{} `json:"-"`
}
type Person struct {
BasePerson
Statuses map[string]Status
}
That way, you can just unmarshal the known fields directly into the BasePerson type, assign it, and then handle the map:
func (p *Person) UnmarshalJSON(data []byte) error {
base := BasePerson{}
if err := json.Unmarshal(data, &base); err != nil {
return err
}
p.BasePerson = base // takes care of all known fields
unknown := map[string]interface{}{}
if err := json.Unmarshal(data, unknown); err != nil {
return err
}
// handle status stuff same as before
delete(unknown, "name") // remove known fields
// marshal unknown key map, then unmarshal into p.Statuses
}
Demo 2
This is how I'd go about it. It allows for calls to json.Marshal and json.Unmarshal to look just like any other type, it centralises the handling of unknown fields in a single place (the implementation of the marshaller/unmarshaller interface), and leaves you with a single Person type where every field contains the required data, in a usable format. It's a tad inefficient in that it relies on unmarshalling/marshalling/unmarshalling the unknown keys. You could do away with that, like I said, using type assertions and iterating over the unknown map instead, faffing around with something like this:
for k, v := range unknown {
m, ok := v.(map[string]interface{})
if !ok {
continue // not {"status": bool}
}
s, ok := m["status"]
if !ok {
continue // status key did not exist, ignore
}
if sb, ok := s.(bool); ok {
// ok, we have a status bool value
p.Statuses[k] = Status{
Status: sb,
}
}
}
But truth be told, the performance difference won't be that great (it's micro optimisation IMO), and the code is a tad too verbose to my liking. Be lazy, optimise when needed, not whenever
Type doesn't meet with your json value.
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
For above json your code should look like below snnipet to work (some modifications in your existing code).
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
type bvu62fu6dq struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status bvu62fu6dq `json:"bvu62fu6dq"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(person)
fmt.Println(person.Status)
}
}
Based on your json data you have to map with type fields.
Run code snippet

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"`
}

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.

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"`
}

Golang modify json without struct

type Struct struct {
Value string `json:"value"`
Value1 string `json:"value_one"`
Nest Nested `json:"nest"`
}
type Nested struct {
Something string `json:"something"`
}
I want to add elements which are not in the structs definitions without creating another struct type. For example
Struct.Extra1 = Nested{"yy"}
Struct.Nested.Extra2 = "zz"
Which will result
{
"Value": "xx",
"Value1": "xx",
"Extra1": {
"Something", "yy"
},
"Nest": {
"Something": "xx",
"Extra2": "zz"
}
}
SOLUTION1:
I thought of adding omitempty to achieve this but it makes the structs complex.
type Struct struct {
Value string
Value1 string
Nest Nested
Extra1 Nested `json:"omitempty"`
}
type Nested struct {
Something string
Extra2 string `json:"omitempty"`
}
SOLUTION2:
myextras := make(map[string]interface{})
// get Struct.Nested in map[string]interface{} format
myextras = Struct.Nest
myextras["Extra2"] = "zz"
// get Struct in map[string]interface{} format
struct["Nest"] = myextras
struct["Extra1"] = Nested{"yy"}
// solves the problem with lots of type casting but doesn't support json tag naming
Is there a better solution to add nested elements which are not represented in struct datatype with json-tag support and could be used to output to user.
If someone is not happy with the solution provided:
Try tidwall/sjson. It provides functions for quick JSON editing without having to define any structure. It saved me a bunch of time yesterday :D
Example usage:
value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.last", "Smith")
println(value)
// Output:
// {"name":{"last":"Smith"}}
Based on this answer: Can I use MarshalJSON to add arbitrary fields to a json encoding in golang?
You could do something like (demo: http://play.golang.org/p/dDiTwxhoNn):
package main
import (
"encoding/json"
"fmt"
"log"
)
type Book struct {
Title string
Author string
// extra is used for additional dynamic element marshalling
extra func() interface{}
}
type FakeBook Book
func (b *Book) SetExtra(fn func() interface{}) {
b.extra = fn
}
func (b *Book) MarshalJSON() ([]byte, error) {
if b.extra == nil {
b.extra = func() interface{} { return *b }
}
return json.Marshal(b.extra())
}
func main() {
ms := &Book{
Title: "Catch-22",
Author: "Joseph Heller",
}
ms.SetExtra(func() interface{} {
return struct {
FakeBook
Extra1 struct {
Something string `json:"something"`
} `json:"extra1"`
}{
FakeBook: FakeBook(*ms),
Extra1: struct {
Something string `json:"something"`
}{
Something: "yy",
},
}
})
out, err := json.MarshalIndent(ms, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
mb := &Book{
Title: "Vim-go",
Author: "Fatih Arslan",
}
mb.SetExtra(func() interface{} {
return struct {
FakeBook
Something string `json:"something"`
}{
FakeBook: FakeBook(*mb),
Something: "xx",
}
})
out, err = json.MarshalIndent(mb, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
mc := &Book{
Title: "Another-Title",
Author: "Fatih Arslan",
}
out, err = json.MarshalIndent(mc, "", " ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
}
yes. there is a type json.Raw which not a struct but []byte. you can manage it out of struct, in any marshal/unmarshal way.
UPDATE
use any way to edit json string on the fly
package main
import (
"fmt"
"encoding/json"
"strings"
)
type Struct struct {
Value string `json:"value"`
Value1 string `json:"value_one"`
Nest json.RawMessage`json:"nest"`
}
func main() {
s := Struct{Value1: "struct string"}
buf, _ := json.Marshal(s)
fmt.Println(string(buf))
s2 := strings.ReplaceAll(string(buf), "null", `{"extra2":{"anykey":3.1415926535}}`)
fmt.Println(s2)
}
https://play.golang.org/p/lOCxJBs5iRJ
The map approach is the only sane way to do it, everything else (like json.RawMessage fields would require an extra marshalling step anyway.
if you don't want install extra package, you can change or add new values with this code :
package main
import "fmt"
import "strings"
import "encoding/json"
func main() {
s := []byte(`{ "level1a":"aaa", "level1b":{"level2a":"bbb", "level2b":{"level3":"aaa"} } }`)
var j interface{}
json.Unmarshal(s, &j)
SetValueInJSON(j, "level1a", "new value 1a")
SetValueInJSON(j, "level1b.level2a", "new value 2a")
SetValueInJSON(j, "level1b.level2b.level3", "new value 3")
SetValueInJSON(j, "level1b.level2c", "new key")
s,_ = json.Marshal(j)
fmt.Println(string(s))
// result: {"level1a":"new value 1a","level1b":{"level2a":"new value 2a","level2b":{"level3":"new value 3"},"level2c":"new key"}}
}
func SetValueInJSON(iface interface{}, path string, value interface{}) interface{} {
m := iface.(map[string]interface{})
split := strings.Split(path, ".")
for k, v := range m {
if strings.EqualFold(k, split[0]) {
if len(split) == 1 {
m[k] = value
return m
}
switch v.(type) {
case map[string]interface{}:
return SetValueInJSON(v, strings.Join(split[1:], "."), value)
default:
return m
}
}
}
// path not found -> create
if len(split) == 1 {
m[split[0]] = value
} else {
newMap := make(map[string]interface{})
newMap[split[len(split)-1]] = value
for i := len(split) - 2; i > 0; i-- {
mTmp := make(map[string]interface{})
mTmp[split[i]] = newMap
newMap = mTmp
}
m[split[0]] = newMap
}
return m
}