unmarshal custom value map - go

I have created a custom type for a map. I would like to unmarshal an array
json response into the map. The key value of the map changes each time the response is received. The issue I have is the unmarshal function does not map correctly to the custom values.
type id map[string]yp
type yp struct {
f1 string
f2 int
}
func main() {
data := []byte("[{\"unique1\":{\"f1\":\"1\",\"f2\":\"2\"}},{\"unique2\":{\"f1\":\"4\",\"f2\":\"7\"}}]")
var i []id
json.Unmarshal(data,&i)
fmt.Printf("%v",i)
}

Since the source value for f2 is string, you need to add a field tag:
package main
import (
"encoding/json"
"fmt"
)
var data = []byte(`
[
{
"unique1": {"f1": "1", "f2": "2"}
}, {
"unique2": {"f1": "4", "f2": "7"}
}
]
`)
func main() {
var ids []map[string]struct {
F1 string
F2 int `json:"f2,string"`
}
json.Unmarshal(data, &ids)
// [map[unique1:{F1:1 F2:2}] map[unique2:{F1:4 F2:7}]]
fmt.Printf("%+v\n", ids)
}

Related

Unexpected newline in composite literal for a map[string]any

I'm new to Go and I try to build a Json-builder functionality to practice. My aim is to create a recursive library to build json.
This is the warning I get for the "second" field.
Unexpected newline in composite literal
and here's my attempt. I don't see a mistake here:
package main
import (
"fmt"
)
type JsonNode struct{
fields map[string]interface{}
}
func BuildJson (fields) JsonNode {
jn := &JsonNode{}
for key,value := range fields {
jn.fields[key] = value
}
return jn
}
func main () {
json := BuildJson(
map[string]any{
"first": 1,
"second": BuildJson(map[string]any{"child": "test"}) // Error for this line.
}
)
fmt.Printf(json)
}
You have multiple errors in your code. This version works, I suggest you use some IDE that report errors prior to compilation (they sometimes fix it for you).
package main
import (
"fmt"
)
type JsonNode struct {
fields map[string]interface{}
}
func BuildJson(fields map[string]any) JsonNode {
jn := &JsonNode{}
jn.fields = make(map[string]interface{})
for key, value := range fields {
jn.fields[key] = value
}
return *jn
}
func main() {
json := BuildJson(
map[string]any{
"first": 1,
"second": BuildJson(map[string]any{"child": "test"}), // Error for this line.
},
)
fmt.Println(json)
}
playground

map output from json array in GO

I need to create a map from a Json array , I started with below in GO , kind of stuck , any pointers ?
package main
import (
"encoding/json"
"fmt"
"strconv"
)
func main() {
jsonStr := `{
"employee": [
{
"id": 14325,
"grpname": "senior"
},
{
"id": 234,
"grpname": "junior"
}
]
}`
type Group struct {
Employee []struct {
GroupName string
GroupId int
}
}
var group []Group
var groupMap []map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &groupMap)
if err != nil {
panic(err)
}
for _, groupData := range groupMap {
// convert map to array of Group struct
var g Group
g.GroupName = fmt.Sprintf("%s", groupData["grpname"])
g.GroupId, _ = strconv.Atoi(fmt.Sprintf("%v", groupData["id"]))
group = append(group, g)
}
fmt.Println(group)
}
Error:
./prog.go:45:4: g.GroupName undefined (type Group has no field or method GroupName)
./prog.go:46:4: g.GroupId undefined (type Group has no field or method GroupId)
Output expected:
{"senior": 14325,"junior": 234}
Tried few things like below , but getting error : There was an error:%!(EXTRA string=json: cannot unmarshal array into Go struct field GetEmpResponse.employee of type map[string][]model.Employee)
type GetEmpResponse struct {
Employee map[string][]Employee json:"employee"
}
Tried to simplify the Json for my testing , please refer to play.golang.org
empResponse.Employee is an array, so you have to access its elements by index, e.g.
empResponse.Employee[0].ID

Slice of type struct with different structs inside it

I'm trying to create the following json below, but I'm not getting it:
json
{
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "foo1",
"displayText": "foo2"
}
},
{
"basicCard": {
"formattedText": "foo3",
"imageDisplayOptions": "CROPPED"
}
}
]
}
}
The problem is that I have a []struct called Items (slice of structs) and it has 2 different structs SimpleResponse and BasicCard and I am not able to mount this json.
The errors appear:
can not use literal SimpleResponse
can not use BasicCard literal
main.go
https://play.golang.org/p/Gbl0UNWhqko
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
type SimpleResponse struct {
TextToSpeech string `json:"textToSpeech"`
DisplayText string `json:"displayText"`
}
type BasicCard struct {
FormattedText string `json:"formattedText"`
ImageDisplayOptions string `json:"imageDisplayOptions"`
}
type Items []struct {
SimpleResponse SimpleResponse `json:"simpleResponse"`
BasicCard BasicCard `json:"basicCard"`
}
type RichResponse struct{
Items Items `json:"items"`
}
group := RichResponse{
Items: Items{
SimpleResponse{"foo1", "foo2"},
BasicCard{
FormattedText: "foo3",
ImageDisplayOptions: "CROPPED",
},
},
}
b, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
}
Could you help me mount this json?
Items is a slice of structs but you're using it as if it's a single Item struct. Being a slice with elements of type Item the correct syntax would be:
group := RichResponse{
Items: Items{ // This is a slice
Item{ // This is an element in the slice
SimpleResponse{"foo1", "foo2"},
BasicCard{
FormattedText: "foo3",
ImageDisplayOptions: "CROPPED",
},
},
},
}
Note that you need to define Item as a named type if you don't want some really messy struct literals. Here's a working example on Playground: https://play.golang.org/p/pzO_w2cIeOJ

Use different structs as a value in map golang

Is there any way to create a map into several structs, and then use It?
I have several different structs that implement the same interface and matching input types for each struct.
I want to read data from the different inputs into the structs - without knowing the input type at the compilation time.
type myInput struct {
InputType string
data []bytes
}
// Will get as an input after compeleation
inputs := []myInput{
myInput{InputType: "a", data: []bytes{0x01, 0x02, 0x03}},
myInput{InputType: "b", data: []bytes{0x01, 0x02}},
}
type StructA struct {
A uint16
B uint32
}
func (a StructA) name () {
fmt.Printf("name is: %d %d", a.A, a.B)
}
type StructB struct {
C uint32
}
func (b StructB) name () {
fmt.Printf("name is: %d", b.C)
}
AorB map[string]<???> {
"a": StructA,
"b": StructB,
}
At this point, I don't know what to do. I need to take the correct struct by the input type and initialize the struct using binary.Read.
for _, myInput := range (inputs) {
// ???? :(
myStruct := AtoB[myInput.InputType]{}
reader :=bytes.NewReader(input1)
err := binary.Read(reader, binary.BigEndian, &myStruct)
fmt.Printf(myStruct.name())
}
Thanks!
Define a interface
type Bin interface {
name() string
set([]byte) // maybe returning error
}
You'll be handling Bins only.
type StructA struct { /* your code so far */ }
type StructB struct { /* your code so far */ }
func (a *StructA) set(b []byte) {
a.A = b[0]<<8 + b[1] // get that right, too lazy to code this for you
a.B = b[2]<<24 + b[3]<<16 + ...
}
// same for StructB
So your StructA/B are Bins now.
func makeBin(in myInput) Bin {
var bin Bin
if in.InputType == "a" {
bin = &StructA{}
} else {
bin = &StructB{}
}
bin.set(in.data) // error handling?
return bin
}
If you have more than two types: Use a switch instead if an if or make a tiny type registry (reflect).
First you define an interface for the commonly used func name:
type Namer interface {
name()
}
Then you can create a map to that interface and insert structs:
AorB := map[string] Namer {
"a": StructA{
A: 42,
B: 28,
},
"b": StructB{
C: 12,
},
}
Now you can access all entries:
for _, n := range AorB {
n.name()
}
You can use interface for the same
AorB := map[string]interface{}{
"a": StructA{},
"b": StructB{},
}
When you retrieve value back you can assert into A for type A and B for type B

Golang - Creating a string from value in array of structs

I have a an array of structs that each have an id and a title.
What is the most efficient way of creating a comma separated list of ids from this array.
eg
Struct A - id: 1, title: ....
Struct B - id: 2, title: ....
Struct C - id: 3, title: ....
Need a string "1,2,3"
Iterate the array and append to a buffer.
package main
import (
"bytes"
"fmt"
"strconv"
)
type data struct {
id int
name string
}
var dataCollection = [...]data{data{1, "A"}, data{2, "B"}, data{3, "C"}}
func main() {
var csv bytes.Buffer
for index, strux := range dataCollection {
csv.WriteString(strconv.Itoa(strux.id))
if index < (len(dataCollection) - 1) {
csv.WriteString(",")
}
}
fmt.Printf("%s\n", csv.String())
}

Resources