I am trying to call an array of objects from my JSON file but I am always facing an error saying: "cannot unmarshal array into Go value of type config.APPConfig".
How can I ensure the configs how my Go struct calls the array of objects within my JSON file?
Here are both my config file in which I set up the Go structs and the JSON file:
Config.go
package config
import (
"encoding/json"
"io/ioutil"
)
type Easy struct {
UID string `json:"uId"`
}
type Auth struct {
Code string `json:"code"`
}
type APPConfig struct {
OpenAPIMode string `json:"openAPIMode"`
OpenAPIURL string `json:"openAPIUrl"`
ClientID string `json:"clientId"`
Secret string `json:"secret"`
AuthMode string `json:"authMode"`
Easy Easy `json:"easy"`
Auth Auth `json:"auth"`
DeviceID string `json:"deviceId"`
UID string `json:"-"`
MQTTUID string `json:"-"`
AccessToken string `json:"-"`
RefreshToken string `json:"-"`
ExpireTime int64 `json:"-"`
}
var App = APPConfig{
OpenAPIMode: "mqtt",
OpenAPIURL: "openapi.tuyacn.com",
}
func LoadConfig() error {
return parseJSON("webrtc.json", &App)
}
func parseJSON(path string, v interface{}) error {
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
err = json.Unmarshal(data, v)
return err
}
JSON file
[
{
"openAPIMode": "mqtt",
"openAPIUrl": "openapi.tuyaus.com",
"clientId": "*****",
"secret": "**************",
"authMode": "easy",
"easy": {
"uId": "**********"
},
"auth": {
"code": ""
},
"deviceId": "***********"
},
{
"openAPIMode": "mqtt",
"openAPIUrl": "openapi.tuyaus.com",
"clientId": "*****",
"secret": "**************",
"authMode": "easy",
"easy": {
"uId": "**********"
},
"auth": {
"code": ""
},
"deviceId": "***********"
}
]
Thanks in advance for helping!
Your config json file is an Array of JSON and you are parsing it to struct you need to parse it to array of struct.
To fix your code change the initialization of App to this.
var App []APPConfig
func LoadConfig() error {
return parseJSON("webrtc.json", &App)
}
Here's example full code for it.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Easy struct {
UID string `json:"uId"`
}
type Auth struct {
Code string `json:"code"`
}
type APPConfig struct {
OpenAPIMode string `json:"openAPIMode"`
OpenAPIURL string `json:"openAPIUrl"`
ClientID string `json:"clientId"`
Secret string `json:"secret"`
AuthMode string `json:"authMode"`
Easy Easy `json:"easy"`
Auth Auth `json:"auth"`
DeviceID string `json:"deviceId"`
UID string `json:"-"`
MQTTUID string `json:"-"`
AccessToken string `json:"-"`
RefreshToken string `json:"-"`
ExpireTime int64 `json:"-"`
}
var App []APPConfig
func LoadConfig() error {
return parseJSON("webrtc.json", &App)
}
func parseJSON(path string, v interface{}) error {
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
err = json.Unmarshal(data, v)
return err
}
func main() {
err := LoadConfig()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", App)
}
Related
I'm using the fsnotify packet to wait for changes in a json file.
I have two problems with this code. The first one is regarding the info returned by ReadFile function. Looks like when I print something returned by the function is empty.
Second issue is regarding the fsnotify that is not reading the file the first time unless i do some modification on the content. I must read the file from the beggining as well.
type Information struct {
Info []Info `json:"info"`
}
type Info struct {
Type string `json:"type"`
News []New `json:"news"`
}
type New struct {
Name string `json:"name"`
Read bool `json:"read"`
}
func ReadFile(file_name string) *Information {
jsonFile, err := os.Open(file_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened file_name.json")
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var infor Information
json.Unmarshal(byteValue, &infor)
return &infor
}
// main function
func main() {
// read json file using fsnotify to wait for changes
watcher, err := fsnotify.NewWatcher()
if err != nil {
panic(err)
}
err = watcher.Add(file_json)
if err != nil {
panic(err)
}
for {
select {
case ev, ok := <-watcher.Events:
log.Println("event:", ev)
if !ok {
return
}
if ev.Op&fsnotify.Write == fsnotify.Write {
data := ReadFile(file_name)
fmt.Print("INFORMATION ABOUT FILE:\n")
for _, info := range data.Info {
fmt.Printf("Info type: %s\n", info.Type) // Here is not printing the result of info.Type
for _, news := range info.News {
fmt.Printf("News Name: %s\n", news.Name) // Here is not printing even "News Name:" or News Read:"
fmt.Printf("News Read: %s\n", strconv.FormatBool(news.Read))
}
}
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}
This is the json file:
{
"info": [
{
"type": "general",
"news": [
{ "name": "abc", "read": true },
{ "name": "def", "read": true }
]
},
{
"type": "confidential",
"news": [
{ "name": "xxx", "read": false },
{ "name": "yyy", "read": false }
]
},
]
}
type Information struct {
Info []Info `json:"info"`
}
type Info struct {
Type string `json:"type"`
News []New `json:"news"` // Here should be a slice define.
}
type New struct {
Name string `json:"name"`
Read bool `json:"read"`
}
func ReadFile(file_name string) *Information {
jsonFile, err := os.Open(file_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened file_name.json")
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var infor Information
json.Unmarshal(byteValue, &infor)
return &infor
}
func main() {
data := ReadFile("./data.json")
for _, news := range data.Info {
for _, v := range news.News {
name := v.Name
// Add you want to do
fmt.Println(name)
}
}
}
You can not get like this:
getInfo = [general][abc, true, def, true]
[confidential][xxx, false, yyy, false]
I am trying to format my response from a server in an array of multiple object JSON. I tried slice and []Struct but I believe I am not able to understand their usage correctly.
I tried the method mentioned here - https://play.golang.org/p/9OEPzbf0Me0 but didn't work in my case
I am using golang to get response for a query parameter
router.HandleFunc("/v1.0/singles", GetInfo).Methods("GET").Queries("Group", "{group}")
//controller.go
func SendToServer(jsonValue []byte, command string) (models.JSON, error) {
var pdatar models.JSON
s := []string{"http://172.xx.xxx.xx:xxxxx/", command}
response, err := http.Post(strings.Join(s, ""), "application/json", bytes.NewBuffer(jsonValue))
data, err := ioutil.ReadAll(response.Body)
err = json.Unmarshal(data, &pdatar)
mdatar := make(models.JSON)
mdatar["ID"] = pdatar["id"]
mdatar["Name"] = pdatar["name"]
fmt.Println(mdatar) //This is how it is coming map[ID:[877,235], Name:[FCU, GHP]]
/*Another attempt
var temp models.Arr
json.Unmarshal(data, &temp)
fmt.Println(temp) ---- returning null
*/
return mdatar, nil
}
func (c Controller) GetSingle(pdata models.Single) (models.JSON, error) {
mdata := make(models.JSON)
mdata["group"] = pdata.Group
jsonValue, err := json.Marshal(mdata)
pdatar, err := SendToServer(jsonValue, "func_info")
return pdatar, nil
// json.NewEncoder(w).Encode(pdatar) //sending this to my main function
}
//model.go
type JSON map[string]interface{}
type Single struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Data []Single
type Arr [][]string
I have a response from my API in a format
{
"id":[877,235,312,429],
"name" ["FCU","GHP","c","d"],
"group":["x","x","y","z"]
}
With Current Code I am receiving a response
{"id":[877 235], "name": ["FCU" "GHP"]
Though I am expecting a response
{
"data":
[
{"id": 877
"name" : "FCU"
}
{"id": 235
"name": "GHP"
}
]
}
Your mdatarr doesn't change the format any, so this result makes sense.
If the data received from the server is in the format:
{
"id": [877,235,312,429],
"name" ["FCU","GHP","c","d"],
"group": ["x","x","y","z"]
}
then you will need translate it. Ideally you'd fix the server so that it sends data in the format:
{
{
"id": 877,
"name": "FCU",
"group": "x",
},
...
}
If the server could send something like the above, then you could simply unmarshal it into a models.Data object.
If that is not an option, and you really need to do the translation client-side, then you'll need a for loop that does something like this:
ids := pdatarr["id"]
names := pdatarr["name"]
...
if len(ids) != len(names) {
return nil, errors.New("Invalid input format")
}
var mdatar models.Data
for i := range(ids) {
mdatar = append(mdatar, models.Single{ids[i], names[i], ...})
}
return models.JSON{"data": mdatar}, nil
My input json data is this (cannot be changed, from an external resource):
[{
"Url": "test.url",
"Name": "testname"
},{
"FormName": "Test - 2018",
"FormNumber": 43,
"FormSlug": "test-2018"
}]
I have two structs that will always match the data within the array:
type UrlData struct{
"Url" string `json:Url`
"Name" string `json:Name`
}
type FormData struct{
"FormName" string `json:FormName`
"FormNumber" string `json:FormNumber`
"FormSlug" string `json:FormSlug`
}
Obviously the code below will not work, but is it possible at the top level (or otherwise) to declare something like this:
type ParallelData [
urlData UrlData
formData FormData
]
Use a two step process for unmarshaling. First, unmarshal a list of arbitrary JSON, then unmarshal the first and second element of that list into their respective types.
You can implement that logic in a method called UnmarshalJSON, thus implementing the json.Unmarshaler interface. This will give you the compound type you are looking for:
type ParallelData struct {
UrlData UrlData
FormData FormData
}
// UnmarshalJSON implements json.Unmarshaler.
func (p *ParallelData) UnmarshalJSON(b []byte) error {
var records []json.RawMessage
if err := json.Unmarshal(b, &records); err != nil {
return err
}
if len(records) < 2 {
return errors.New("short JSON array")
}
if err := json.Unmarshal(records[0], &p.UrlData); err != nil {
return err
}
if err := json.Unmarshal(records[1], &p.FormData); err != nil {
return err
}
return nil
}
Try it on the playground: https://play.golang.org/p/QMn_rbJj-P-
I think Answer of Peter is awesome.
Option 1:
type ParallelData [
urlData UrlData
formData FormData
]
if you need above structure then you can define it as
type UrlData struct {
Url string `json:"Url,omitempty"`
Name string `json:"Name,omitempty"`
}
type FormData struct {
FormName string `json:"FormName,omitempty"`
FormNumber string `json:"FormNumber,omitempty"`
FormSlug string `json:"FormSlug,omitempty"`
}
type ParallelData struct {
UrlData UrlData `json:"UrlData,omitempty"`
FormData FormData `json:"FormData,omitempty"`
}
In this case, your json will look like
[
{
"UrlData":{
"Url":"test.url",
"Name":"testname"
}
},
{
"FormData":{
"FormName":"Test - 2018",
"FormNumber":"43",
"FormSlug":"test-2018"
}
}
]
Option 2:
You've provide following json:
[
{
"Url":"test.url",
"Name":"testname"
},
{
"FormName":"Test - 2018",
"FormNumber":43,
"FormSlug":"test-2018"
}
]
If your json really look like, then you can use following struct
type UrlData struct {
Url string `json:Url`
Name string `json:Name`
}
type FormData struct {
FormName string `json:FormName`
FormNumber int `json:FormNumber`
FormSlug string `json:FormSlug`
}
type ParallelData struct {
UrlData
FormData
}
For both options, you can Unmarshall your json like this
var parallelData []ParallelData
err := json.Unmarshal([]byte(str), ¶llelData)
if err != nil {
panic(err)
}
fmt.Println(parallelData)
See option 1 in playground
See option 2 in playground
You can unmarshal into a map[string]interface{} for example:
type ParallelData map[string]interface{}
func main() {
textBytes := []byte(`[
{
"Url": "test.url",
"Name": "testname"
},
{
"FormName": "Test - 2018",
"FormNumber": 43,
"FormSlug": "test-2018"
}]`)
var acc []ParallelData
json.Unmarshal(textBytes, &acc)
fmt.Printf("%+v", acc)
}
Output:
=> [map[Url:test.url Name:testname] map[FormName:Test - 2018 FormNumber:43 FormSlug:test-2018]]
Playground
My input json data is this (cannot be changed, from an external resource):
[{
"Url": "test.url",
"Name": "testname"
},{
"FormName": "Test - 2018",
"FormNumber": 43,
"FormSlug": "test-2018"
}]
I have two structs that will always match the data within the array:
type UrlData struct{
"Url" string `json:Url`
"Name" string `json:Name`
}
type FormData struct{
"FormName" string `json:FormName`
"FormNumber" string `json:FormNumber`
"FormSlug" string `json:FormSlug`
}
Obviously the code below will not work, but is it possible at the top level (or otherwise) to declare something like this:
type ParallelData [
urlData UrlData
formData FormData
]
Use a two step process for unmarshaling. First, unmarshal a list of arbitrary JSON, then unmarshal the first and second element of that list into their respective types.
You can implement that logic in a method called UnmarshalJSON, thus implementing the json.Unmarshaler interface. This will give you the compound type you are looking for:
type ParallelData struct {
UrlData UrlData
FormData FormData
}
// UnmarshalJSON implements json.Unmarshaler.
func (p *ParallelData) UnmarshalJSON(b []byte) error {
var records []json.RawMessage
if err := json.Unmarshal(b, &records); err != nil {
return err
}
if len(records) < 2 {
return errors.New("short JSON array")
}
if err := json.Unmarshal(records[0], &p.UrlData); err != nil {
return err
}
if err := json.Unmarshal(records[1], &p.FormData); err != nil {
return err
}
return nil
}
Try it on the playground: https://play.golang.org/p/QMn_rbJj-P-
I think Answer of Peter is awesome.
Option 1:
type ParallelData [
urlData UrlData
formData FormData
]
if you need above structure then you can define it as
type UrlData struct {
Url string `json:"Url,omitempty"`
Name string `json:"Name,omitempty"`
}
type FormData struct {
FormName string `json:"FormName,omitempty"`
FormNumber string `json:"FormNumber,omitempty"`
FormSlug string `json:"FormSlug,omitempty"`
}
type ParallelData struct {
UrlData UrlData `json:"UrlData,omitempty"`
FormData FormData `json:"FormData,omitempty"`
}
In this case, your json will look like
[
{
"UrlData":{
"Url":"test.url",
"Name":"testname"
}
},
{
"FormData":{
"FormName":"Test - 2018",
"FormNumber":"43",
"FormSlug":"test-2018"
}
}
]
Option 2:
You've provide following json:
[
{
"Url":"test.url",
"Name":"testname"
},
{
"FormName":"Test - 2018",
"FormNumber":43,
"FormSlug":"test-2018"
}
]
If your json really look like, then you can use following struct
type UrlData struct {
Url string `json:Url`
Name string `json:Name`
}
type FormData struct {
FormName string `json:FormName`
FormNumber int `json:FormNumber`
FormSlug string `json:FormSlug`
}
type ParallelData struct {
UrlData
FormData
}
For both options, you can Unmarshall your json like this
var parallelData []ParallelData
err := json.Unmarshal([]byte(str), ¶llelData)
if err != nil {
panic(err)
}
fmt.Println(parallelData)
See option 1 in playground
See option 2 in playground
You can unmarshal into a map[string]interface{} for example:
type ParallelData map[string]interface{}
func main() {
textBytes := []byte(`[
{
"Url": "test.url",
"Name": "testname"
},
{
"FormName": "Test - 2018",
"FormNumber": 43,
"FormSlug": "test-2018"
}]`)
var acc []ParallelData
json.Unmarshal(textBytes, &acc)
fmt.Printf("%+v", acc)
}
Output:
=> [map[Url:test.url Name:testname] map[FormName:Test - 2018 FormNumber:43 FormSlug:test-2018]]
Playground
I am trying to get each JSON object out of a JSON array. I get this data via a HTTP post.
I know what my data will look like:
{
"array":[
{
"entity_title":"University of Phoenix",
"entity_org_name":"CS Club",
"possible_user_name":"Johnny Ive",
"posibble_user_email":"Johhny.Ive#uop.edu",
"user_position_title":"President",
"msg_body_id":4
},
{
"entity_title":"University of San Francisco",
"entity_org_name":"Marketing club",
"possible_user_name":"steve jobs",
"posibble_user_email":"steven.job#uop.edu",
"user_position_title":"Student",
"msg_body_id":5
}
]
}
My example code and my structs look like this:
type MsgCreateUserArray struct {
CreateUser []MsgCreateUserJson `json:"createUserArray"`
}
type MsgCreateUserJson struct {
EntityTitleName string `json:"entity_title_name"`
EntityOrgName string `json:"entity_org_name"`
PossibleUserName string `json:"possible_user_name"`
PossibleUserEmail string `json:"possible_user_email"`
UserPositionTitle string `json:"user_position_title"`
MsgBodyId string `json:"msg_body_id, omitempty"`
}
func parseJson(rw http.ResponseWriter, request *http.Request) {
decodeJson := json.NewDecoder(request.Body)
var msg MsgCreateUserArray
err := decodeJson.Decode(&msg)
if err != nil {
panic(err)
}
log.Println(msg.CreateUser)
}
func main() {
http.HandleFunc("/", parseJson)
http.ListenAndServe(":1337", nil)
}
I am not sure where how to iterate over the JSON array and get the JSON objects and then just work with the JSON objects.
Try this as your structs,
type MsgCreateUserArray struct {
CreateUser []MsgCreateUserJson `json:"array"`
}
type MsgCreateUserJson struct {
EntityOrgName string `json:"entity_org_name"`
EntityTitle string `json:"entity_title"`
MsgBodyID int64 `json:"msg_body_id,omitempty"`
PosibbleUserEmail string `json:"posibble_user_email"`
PossibleUserName string `json:"possible_user_name"`
UserPositionTitle string `json:"user_position_title"`
}
Your entity_title_name is not named correctly, nor is the top level array. After you decode into a MsgCreateUserArray you can iterate over the CreateUser slice to get each MsgCreateUserJson