Declaring a struct for json objects - json

I have a JSON response object from a API like that:
[
{
"peoples": {
"1": {"name": "Jhon", "age": 123},
"2": {"name": "Jhon", "age": 123},
"3": {"name": "Jhon", "age": 123},
"4": {"name": "Jhon", "age": 123},
"_object": true,
"_timestamp": "2020-08-05T07:05:55.509Z",
"_writable": false
}
}
]
The parameters: peoples, _object, _timestamp and _writable is fixed. The dynamic values are the 1,2,3,4...n parameters.
The qty of peoples in that struct can be more then 4 or can be 1. Have any elegant solution for create a Struct object or a json.Unmarshal for that?

Borrowing the input example from Sarath Sadasivan Pillai (see comment), here (link to Playground example) is a way to do it with map[string]json.RawMessage and a custom unmarshal function:
package main
import (
"encoding/json"
"fmt"
"strconv"
"time"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
type Keywords struct {
Object bool `json:"_object"`
Timestamp time.Time `json:"_timestamp"`
Writable bool `json:"_writable"`
}
type Decoded struct {
People map[string]Person // or perhaps just []Person
Info Keywords
}
var input []byte = []byte(`{
"1": {"name": "Jhon", "age": 123},
"2": {"name": "Jhon", "age": 123},
"3": {"name": "Jhon", "age": 123},
"4": {"name": "Jhon", "age": 123},
"_object": true,
"_timestamp": "2020-08-05T07:05:55.509Z",
"_writable": false
}`)
// Unmarshal json in the input format outlined by
// the example above: a map of numeric strings to Name/Age pair,
// plus some keywords.
func (d *Decoded) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &d.Info); err != nil {
return err
}
var m map[string]json.RawMessage
if err := json.Unmarshal(data, &m); err != nil {
return err
}
if d.People == nil {
d.People = make(map[string]Person)
}
for k := range m {
// This is the hard part: you must choose how
// to decide whether to decode this as a Person.
// Here, we use strconv.Atoi() as in the example
// by Sarath Sadasivan Pillai, but there are as
// many options as you can think of.
//
// For instance, another method would be to try
// decoding the json.RawMessage as a Person. It's
// also not clear whether the numeric values imply
// some particular ordering, which maps discard.
// (If these come out in order, that's just luck.)
if _, err := strconv.Atoi(k); err != nil {
continue
}
var p Person
if err := json.Unmarshal(m[k], &p); err != nil {
return err
}
d.People[k] = p
}
return nil
}
func main() {
var x Decoded
err := json.Unmarshal(input, &x)
if err != nil {
fmt.Printf("failed: %v\n", err)
} else {
fmt.Printf("succeeded:\n%#v\n", x.Info)
for k := range x.People {
fmt.Printf("person %q = %#v\n", k, x.People[k])
}
}
}

Related

how to convert map[string]interface{} data to struct

I do not know how to ask, so i will ask with an example.
I have some data like that
{
..
"velocityStatEntries": {
"8753": {
"estimated": {"value": 23.0,"text": "23.0"},
"completed": {"value": 27.0,"text": "27.0"}
},
"8673": {
"estimated": {"value": 54.5,"text": "54.5"},
"completed": {"value": 58.5,"text": "58.5"}
},
.
.
.
}
..
}
I want to declare a type that takes map key to its "KEY" or any property that is given by me.
Is it possible without using map iteration?
Expected output:
{...
"velocityStatEntries": {
{
"key": "8753",
"estimated": {"value": 54.5,"text": "54.5"},
"completed": {"value": 58.5,"text": "58.5"}
},
{
"key": "8673",
"estimated": {"value": 54.5,"text": "54.5"},
"completed": {"value": 58.5,"text": "58.5"}
},
}
...
}
This is what i have done
type VelocityStatEntry struct {
Key string
Estimated struct {
Value float64 `json:"value"`
Text string `json:"text"`
} `json:"estimated"`
Completed struct {
Value float64 `json:"value"`
Text string `json:"text"`
} `json:"completed"`
}
type RapidChartResponse struct {
...
VelocityStatEntries map[string]VelocityStatEntry `json:"velocityStatEntries"`
..
}
But it is not working. I want to take that string map key to KEY property.
If the data originates from JSON then you should skip the map[string]interface{} and instead use a custom unmarshaler implemented by your desired struct that does what you want. Perhaps by utilizing a map[string]json.RawMessage. But map[string]interface{} to struct conversion is a pain, avoid it if possible.
For example:
type VelocityStatEntryList []*VelocityStatEntry
func (ls *VelocityStatEntryList) UnmarshalJSON(data []byte) error {
var m map[string]json.RawMessage
if err := json.Unmarshal(data, &m); err != nil {
return err
}
for k, v := range m {
e := &VelocityStatEntry{Key: k}
if err := json.Unmarshal([]byte(v), e); err != nil {
return err
}
*ls = append(*ls, e)
}
return nil
}
https://go.dev/play/p/VcaW_BWXRVr

Golang unmarshaling JSON to protobuf generated structs

I would like to reqeive a JSON response withing a client application and unmarshal this response into a struct. To ensure that the struct stays the same accross all client apps using this package, I would like to define the JSON responses as protobuf messages. I am having difficulties unmarshaling the JSON to the protobuf generated structs.
I have the following JSON data:
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
I have modeled my protobuf definitions like this:
syntax = "proto3";
package main;
message Container {
string name = 1;
string type = 2;
}
message Containers {
repeated Container containers = 1;
}
Using this pattern with structs normaly works, but for some reason using these proto definitions causes issues. The below code demonstrates a working and a non-working example. Although one of the versions work, I am unable to use this solution, since []*Container does not satisfy the proto.Message interface.
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/gogo/protobuf/jsonpb"
)
func working(data string) ([]*Container, error) {
var cs []*Container
return cs, json.Unmarshal([]byte(data), &cs)
}
func notWorking(data string) (*Containers, error) {
c := &Containers{}
jsm := jsonpb.Unmarshaler{}
if err := jsm.Unmarshal(strings.NewReader(data), c); err != nil {
return nil, err
}
return c, nil
}
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
w, err := working(data)
if err != nil {
panic(err)
}
fmt.Print(w)
nw, err := notWorking(data)
if err != nil {
panic(err)
}
fmt.Print(nw.Containers)
}
Running this gives the following output:
[name:"C1" type:"docker" name:"C2" type:"docker" ]
panic: json: cannot unmarshal array into Go value of type map[string]json.RawMessage
goroutine 1 [running]:
main.main()
/Users/example/go/src/github.com/example/example/main.go:46 +0x1ee
Process finished with exit code 2
Is there a way to unmarshal this JSON to Containers? Or alternatively, make []*Container to satisfy the proto.Message interface?
For the message Containers, i.e.
message Containers {
repeated Container containers = 1;
}
The correct JSON should look like:
{
"containers" : [
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
}
If you cannot change the JSON then you can utilize the func that you've created
func working(data string) ([]*Container, error) {
var cs []*Container
err := json.Unmarshal([]byte(data), &cs)
// handle the error here
return &Containers{
containers: cs,
}, nil
}
You should use NewDecoder to transfer the data to jsonDecoder and then traverse
the array.The code is this
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
jsonDecoder := json.NewDecoder(strings.NewReader(data))
_, err := jsonDecoder.Token()
if err != nil {
log.Fatal(err)
}
var protoMessages []*pb.Container
for jsonDecoder.More() {
protoMessage := pb.Container{}
err := jsonpb.UnmarshalNext(jsonDecoder, &protoMessage)
if err != nil {
log.Fatal(err)
}
protoMessages = append(protoMessages, &protoMessage)
}
fmt.Println("%s", protoMessages)
}

Golang: unmarshal json object into a slice

I have JSON like
{
"a": {"key": "a", "value": 1,},
"b": {"key": "b", "value": 1,},
}
Is there a way to unmarshal it into []*struct {Key string; Value int}, preserving the order of the structures?
If I unmarshal the JSON into map[string]*struct {Key string; Value int} and then convert the map into a slice, I'll lose the order of the structures.
To preserve order, use Decoder.Token and Decoder.More to walk through the top-level JSON object.
r := strings.NewReader(`
{
"a": {"key": "a", "value": 1},
"b": {"key": "b", "value": 1}
}`)
d := json.NewDecoder(r)
t, err := d.Token()
if err != nil || t != json.Delim('{') {
log.Fatal("expected object")
}
var result []*element
for d.More() {
k, err := d.Token()
if err != nil {
log.Fatal(err)
}
var v element
if err := d.Decode(&v); err != nil {
log.Fatal(err)
}
result = append(result, &v)
fmt.Println("key:", k, "value:", v)
}
Run it on the Go Playground
Change the calls to log.Fatal to the error handling appropriate for your application.
This answer edits the JSON in the question to make the JSON valid.
The field names in the struct element type must be exported.
The easiest way that I found was to use jsonparser.ObjectEach:
import "github.com/buger/jsonparser"
...
var ss []*struct{Key string; Value int}
err = jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
var s struct{Key string; Value int}
if err := json.Unmarshal(value, &s); err != nil {
return err
}
*ss = append(*ss, &s)
return nil
})
You could use the map[string]interface{} to unmarshal the json string.
The code is this
func test() {
jsonStr := `
{
"a": {"key": "a", "value": 1},
"b": {"key": "b", "value": 1}
}`
var mapResult map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &mapResult)
if err != nil {
fmt.Println("JsonToMapDemo err: ", err)
}
fmt.Println(mapResult)
}
the output is:
map[a:map[key:a value:1] b:map[key:b value:1]]

Unmarshalling json arrays as json objects

I have to unmarshal a series of Json objects, but one of the objects contain a json array which is not really structured in a good way.
"labels": [
{
"key": "owner",
"value": "harry"
},
{
"key": "group",
"value": "student"
}
]
I am unmarshalling it using this struct -
type StudentDetails struct {
Id string `json:"id"`
Name string `json:"name"`
Labels []Label `json:"labels,omitempty"`
}
type Label struct {
Key string `json:"key"`
Value string `json:"value"`
}
And I have to access it using x.Labels[0].key == "owner" inside a for loop which is very annoying.
I want to be able to do x.Labels.Owner == "harry" instead. How do I go about achieving this? The rest of JSON is unmarshalled fine using the default unmarshal function, so I don't think writing custom function will be good option.
With the constraints you have here, this is about as close as you will get (run in playground):
package main
import (
"encoding/json"
"fmt"
)
func main() {
j := `
{
"id": "42",
"name": "Marvin",
"labels": [
{
"key": "owner",
"value": "harry"
},
{
"key": "group",
"value": "student"
}
]
}`
d := StudentDetails{}
err := json.Unmarshal([]byte(j), &d)
if err != nil {
panic(err)
}
fmt.Println(d.Labels["owner"])
fmt.Println(d.Labels["group"])
}
type StudentDetails struct {
Id string `json:"id"`
Name string `json:"name"`
Labels Labels `json:"labels"`
}
type Labels map[string]string
func (l *Labels) UnmarshalJSON(b []byte) error {
a := []map[string]string{}
err := json.Unmarshal(b, &a)
if err != nil {
return err
}
t := map[string]string{}
for _, m := range a {
t[m["key"]] = m["value"]
}
*l = t
return nil
}
How about to define custom []Label type and add function on it.
For instance
type Labels []Label
func (l Labels) Owner() string {
if len(l) > 1 {
return l[0].Value
}
return ""
}

Custom config loading in Go

I'm using JSON files to store/load my config. Let's say I have the following:
type X interface
// implements interface X
type Y struct {
Value string
}
// implements interface X
type Z struct {
Value string
}
type Config struct {
interfaceInstance X `json:"X"`
}
Config file example:
{
"config1": {
"X": {
"type": "Z",
"Value": "value_1"
}
},
"config2": {
"X": {
"type": "Y",
"Value": "value_2"
}
}
}
I want to be able to define config files something like this example, and be able to dynamically load the JSON as either struct Y or struct Z. Any suggestions on how to accomplish this? I'm using a simple json.Decoder to load the JSON as a struct.
decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
One possible strategy would be to implement json.Unmarshaler for the Config type in such a way that you first unmarshal into a generic object and inspect the "type" attribute then, switching on the type string, unmarshal the same byte array into the known type and assign to the "interfaceInstance" member of the config.
For example (Go Playground):
// Note the slightly different JSON here...
var jsonstr = `{
"config1": {
"type": "Z",
"Value": "value_1"
},
"config2": {
"type": "Y",
"Value": "value_2"
}
}`
func main() {
config := map[string]Config{}
err := json.Unmarshal([]byte(jsonstr), &config)
if err != nil {
panic(err)
}
fmt.Printf("OK: %#v\n", config)
// OK: map[string]main.Config{
// "config1": main.Config{interfaceInstance:main.Z{Value:"value_1"}},
// "config2": main.Config{interfaceInstance:main.Y{Value:"value_2"}},
// }
}
func (c *Config) UnmarshalJSON(bs []byte) error {
// Unmarshal into an object to inspect the type.
var obj map[string]interface{}
err := json.Unmarshal(bs, &obj)
if err != nil {
return err
}
// Unmarshal again into the target type.
configType := obj["type"].(string)
switch configType {
case "Y":
var y Y
if err = json.Unmarshal(bs, &y); err == nil {
c.interfaceInstance = y
}
case "Z":
var z Z
if err = json.Unmarshal(bs, &z); err == nil {
c.interfaceInstance = z
}
default:
return fmt.Errorf("unexpected type %q", configType)
}
return err
}