How to loop on nested structure in golang? - go

I have a nested struct and I want to loop over this struct. Please help me how to loop over the struct to get the output in this format. I have mentioned the struct which I am using.
Expected Output:
{
"PrefcatID":"PREF_001"
"prefname: :"PREF_name"
"PrefSubcategory":
{
"subcatid":"SUB_PREF_001",
"PrefcatID":"PREF_001",
"subcatname":"Sub Category Name 1"
},
{
"subcatid":"SUB_PREF_002",
"PrefcatID":"PREF_001",
"subcatname":"Sub Category Name 2"
}
}
Struct in Go:
type PrefCategory struct {
PrefcatID string `json:"PrefcatID"`
PrefName string `json:"prefname"`
Temp_PrefSubcategory []PrefSubcategory `json:"prefSubcategory "`
}
type PrefSubcategory struct {
PrefcatID string `json:"PrefcatID"`
SubcatId string `json:"subcatid"`
SubCatName string `json:"subcatname"`
}

You're almost there but note that there are some syntax errors in your JSON example. The structs are almost there too, ultimately, you just need to build an instance of PrefCategory with the expected values and marshal it to JSON:
type PrefCategory struct {
PrefcatID string `json:"PrefcatID"`
PrefName string `json:"prefname"`
Temp_PrefSubcategory []PrefSubcategory `json:"prefSubcategory"`
}
type PrefSubcategory struct {
PrefcatID string `json:"PrefcatID"`
SubcatId string `json:"subcatid"`
SubCatName string `json:"subcatname"`
}
func main() {
pref := PrefCategory{
"PREF_001",
"PREF_name",
[]PrefSubcategory{
{"SUB_PREF_001", "PREF_001", "Subcategory Name 1"},
{"SUB_PREF_002", "PREF_001", "Subcategory Name 2"},
},
}
jsonbytes, err := json.MarshalIndent(&pref, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(jsonbytes))
/*
{
"PrefcatID": "PREF_001",
"prefname": "PREF_name",
"prefSubcategory": [
{
"PrefcatID": "SUB_PREF_001",
"subcatid": "PREF_001",
"subcatname": "Subcategory Name 1"
},
{
"PrefcatID": "SUB_PREF_002",
"subcatid": "PREF_001",
"subcatname": "Subcategory Name 2"
}
]
}
*/
}

Related

How to parse JSON-RPC table with different type

I want get informations in JSON-RPC file with this structure :
{
"id": "foo1",
"error": null,
"result": [
{
"key": [
"hello 1",
1,
"world 1"
],
"val": {
"type": "static"
}
},
{
"key": [
"hello 2",
1,
"world 2"
],
"val": {
"type": "static"
}
}
]
}
This is my parsing function, Key is string table (can't accept int type) :
type JsonRpcRsp struct {
Id string `json:"id"`
Error *string `json:"error"`
Result json.RawMessage `json:"result"`
}
type JsonRpcEntry_Val struct {
Type string `json:"type"`
}
type JsonRpcEntry struct {
Key [3]string `json:"key"`
Val JsonRpcEntry_Val `json:"val"`
}
jsonResult := JsonRpcRsp{}
json.Unmarshal(data, &jsonResult)
entries := []JsonRpcEntry{}
for _, val := range jsonResult {
json.Unmarshal(val.Result, &entries)
}
How to parse "key" table ?... problem is there are different types
key table structure is :
[ <string>, <int>, <string>]
To unmarshal arrays of different types in Go you'll need to use interfaces and consequently type assertions if you need access to the types.
This will work for you:
type Result struct {
Key [3]interface{} `json:"key"`
Val struct {
Type string `json:"type"`
} `json:"val"`
}
msg := JsonRpcRsp{}
json.Unmarshal(data, &msg)
var result []Result
json.Unmarshal(msg.Result, &result)
for _, v := range result {
key1 := v.Key[0].(string)
key2 := v.Key[1].(float64)
key3 := v.Key[2].(string)
fmt.Println(key1, key2, key3)
}
After asserting the three interfaces to their types, you can then work with them further, depending on your use case.

Read json array with json.NewDecoder

How do I get a json array (named list in the json file) to a list using json.NewDecoder? My struct looks like this:
type Config struct {
Data1 struct {
Host string `json:"host"`
Port string `json:"port"`
} `json:"data1"`
Data2 struct {
Host string `json:"host"`
Port string `json:"port"`
} `json:"data2"`
List struct {
Items []string
} `json:"list"`
}
and I'm parsing like this:
jsonParser := json.NewDecoder(configFile)
jsonParser.Decode(&config)
my config.json looks like this
{
"data1": {
"host": "10.10.20.20",
"port": "1234"
},
"data2": {
"host": "10.10.30.30",
"port": "5678"
},
"list": [
"item 1",
"item 2",
"item 3",
"item 4"
]
}
It's easy when the fields have names but I havn't figured out on how to get the information from the list...
I found a way to resolve your problom. here is the code:
package main
import (
"encoding/json"
"fmt"
"strings"
)
type ConfigWithoutList struct {
Data1 struct {
Host string `json:"host"`
Port string `json:"port"`
} `json:"data1"`
Data2 struct {
Host string `json:"host"`
Port string `json:"port"`
} `json:"data2"`
}
type Config struct {
ConfigWithoutList
List struct {
Items []string
} `json:"list"`
}
func (u *Config) UnmarshalJSON(data []byte) error {
aux := struct {
List []string `json:"list"`
ConfigWithoutList
}{}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
u.List = struct {
Items []string
}{
Items: aux.List,
}
return nil
}
func main() {
const jsonStream = `{
"data1": {
"host": "10.10.20.20",
"port": "1234"
},
"data2": {
"host": "10.10.30.30",
"port": "5678"
},
"list": [
"item 1",
"item 2",
"item 3",
"item 4"
]
}
`
config := Config{}
jsonParser := json.NewDecoder(strings.NewReader(jsonStream))
jsonParser.Decode(&config)
fmt.Println(config.List) // output => {[item 1 item 2 item 3 item 4]}
}

How to get the number of items from a structure sub field using reflect in go

I have some issues when getting the number of items from a sub field in a slice struct through reflect package.
This is how I'm trying to get the number of items from Items
func main() {
type Items struct {
Name string `json:"name"`
Present bool `json:"present"`
}
type someStuff struct {
Fields string `json:"fields"`
Items []Items `json:"items"`
}
type Stuff struct {
Stuff []someStuff `json:"stuff"`
}
some_stuff := `{
"stuff": [
{
"fields": "example",
"items": [
{ "name": "book01", "present": true },
{ "name": "book02", "present": true },
{ "name": "book03", "present": true }
]
}
]
}`
var variable Stuff
err := json.Unmarshal([]byte(some_stuff), &variable)
if err != nil {
panic(err)
}
//I want to get the number of items in my case 3
NumItems := reflect.ValueOf(variable.Stuff.Items)
}
This is the error:
variable.Items undefined (type []Stuff has no field or method Items)
I'm unsure if I can retrieve the number of items like that.
I have already fixed the issue.
In order to get the number of sub fields we can make use of Len() from reflect.ValueOf.
The code now is getting the number of Items:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
type Items struct {
Name string `json:"name"`
Present bool `json:"present"`
}
type someStuff struct {
Fields string `json:"fields"`
Items []Items `json:"items"`
}
type Stuff struct {
Stuff []someStuff `json:"stuff"`
}
some_stuff := `{
"stuff": [
{
"fields": "example",
"items": [
{ "name": "book01", "present": true },
{ "name": "book02", "present": true },
{ "name": "book03", "present": true }
]
}
]
}`
var variable Stuff
err := json.Unmarshal([]byte(some_stuff), &variable)
if err != nil {
panic(err)
}
//I want to get the number of items in my case 3
t := reflect.ValueOf(variable.Stuff[0].Items)
fmt.Println(t.Len())
}
Output: 3

Convert JSON key value pair into single field value in Go

I have a json like following, where value can be int or string
{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}
Now I want to convert that json into following struct, like only extract a_name and b_name value.
type Data struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
I can do it by following way
import (
"encoding/json"
"fmt"
)
type TmpData struct {
Data []struct {
Name string `json:"Name"`
Value interface{} `json:"value"`
} `json:"data"`
}
type ExpectedData struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
func main() {
data := `{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}`
tmpData := &TmpData{}
json.Unmarshal([]byte(data), tmpData)
ans := &ExpectedData{}
for _, d := range tmpData.Data {
if d.Name == "a_name" {
ans.AName = int(d.Value.(float64))
} else if d.Name == "b_name" {
ans.BName = d.Value.(string)
}
}
fmt.Println(ans)
}
Is there any better solution for this?
Not possible with the standard JSON un-marshalling unless you write a custom un-marshaller for your Data type.
The key here is to define the type for value to be an interface{}, so that multiple types could be stored in your b_name record.
func (d *Data) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _, value := range result.Data {
switch value.Name {
// The json package will assume float64 when Unmarshalling with an interface{}
case "a_name":
v, ok := (value.Value).(float64)
if !ok {
return fmt.Errorf("a_name got data of type %T but wanted float64", value.Value)
}
d.AName = int(v)
case "b_name":
v, ok := (value.Value).(string)
if !ok {
return fmt.Errorf("b_name got data of type %T but wanted string", value.Value)
}
d.BName = v
}
}
return nil
}
Playground - https://go.dev/play/p/GrXKAE87d1F

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

Resources