Read json array with json.NewDecoder - go

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

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.

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

golang unmarshal map[string]interface{} to a struct containing an array with meta

I have the following json data coming through an API. I want to unmarshal this data into a different way of structure as it is defined below. How can I do it in an elegant way?
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
// It goes on..
}
type Data struct {
Meta Meta `json:"_meta,omitempty"`
Elements []Element
}
type Element struct {
Key string
Name string
}
type Meta struct{
Count int
Total int
}
This can be quite tricky because you have a json object that holds everything. So i went with the approach of unmarshalling to map of string to *json.RawMessage and then fixing the struct from there.
To do that you will be using a custom Unmarshaler and the benefit of it is that you delay the actual parsing of the inner messages until you need them.
So for example if your meta field was wrong or the numbers it said didn't match the length of the map-1 you could exit prematurely.
package main
import (
"encoding/json"
"fmt"
)
type jdata map[string]*json.RawMessage
type data struct {
Meta Meta
Elements []Element
}
//Element is a key val assoc
type Element struct {
Key string
Name string
}
//Meta holds counts and total of elems
type Meta struct {
Count int
Total int
}
var datain = []byte(`
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
}`)
func (d *data) UnmarshalJSON(buf []byte) (err error) {
var (
meta *json.RawMessage
ok bool
)
jdata := new(jdata)
if err = json.Unmarshal(buf, jdata); err != nil {
return
}
if meta, ok = (*jdata)["_meta"]; !ok {
return fmt.Errorf("_meta field not found in JSON")
}
if err = json.Unmarshal(*meta, &d.Meta); err != nil {
return
}
for k, v := range *jdata {
if k == "_meta" {
continue
}
elem := &Element{}
if err = json.Unmarshal(*v, elem); err != nil {
return err
}
d.Elements = append(d.Elements, *elem)
}
return nil
}
func main() {
data := &data{}
if err := data.UnmarshalJSON(datain); err != nil {
panic(err)
}
fmt.Printf("decoded:%v\n", data)
}

How to loop on nested structure in golang?

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"
}
]
}
*/
}

Resources