JSON unmarshal in nested struct - json

I am trying to unmarshal an incoming JSON into a struct that contains an array of structs. However I get the error
"Invalid input. JSON badly formatted. json: cannot unmarshal array into Go struct field DataInput.Asset of type app.AssetStorage"
I have tried to recreate the code here: https://play.golang.org/p/RuBaBjPmWxO, however I can't reproduce the error (although the incoming message and code are identical).
type AssetStorage struct {
Event string `json:"Event"`
EmployeeID int `json:"EmployeeID"`
EmployeeEmail string `json:"EmployeeEmail"`
PerformedBy string `json:"PerformedBy"`
Timestamp string `json:"Timestamp"`
AlgorithmID string `json:"AlgorithmID"`
AlgorithmHash string `json:"AlgorithmHash"`
Objects []Object `json:"Objects"`
}
type Object struct {
ShortName string `json:"ShortName"`
Hash string `json:"Hash"`
DestroyDate string `json:"DestroyDate"`
}
type DataInput struct {
Username string
Token string `json:"Token"`
Asset AssetStorage `json:"Asset"`
}
func main() {
var data DataInput
json.Unmarshal(input, data)
data.Username = data.Asset.EmployeeEmail
fmt.Printf("%+v\n", data)
}

There are three bugs in your code, one is that your are not using address of DataInput struct when you are unmarshalling your JSON.
This should be:
var data DataInput
json.Unmarshal(input, data)
like below:
var data DataInput
if err := json.Unmarshal(input, &data); err != nil {
log.Println(err)
}
One Piece of advice in above code. Never skip errors to know more about the error
Next as the error says:
Invalid input. JSON badly formatted. json: cannot unmarshal array into
Go struct field DataInput.Asset of type app.AssetStorage
DataInput.Asset should be an array of json objects there you should change your AssetStorage to []AssetStorage in your declaration in DataInput struct.
One more error is that you are declaring the type of EmployeeID field for AssetStorage struct to be an int which should be a string
Working Code on Go Playground

The answer is in the error message:
Invalid input. JSON badly formatted. json: cannot unmarshal array into Go struct field DataInput.Asset of type app.AssetStorage
The json you're parsing has DataInput.Asset as an array of AssetStorage objects. So, the type needs to be []AssetStorage.

Related

How to set optional json in nest struct

I tried to set an optional json config in nest struct, when i need this json it will appear, otherwise it will not exist.
type Test struct {
Data NestTest `json:"data"`
}
type NestTest struct {
NestData1 string `json:"data1"`
NestData2 string `json:"data2,omitempty"`
}
test := Test{
Data: NestTest{
NestData1: "something",
},
}
b, err := json.Marshal(test)
fmt.Sprintf("the test struct json string is: %s", string(b))
output:
{"data":{"data1":"something","data2":""}}
expect:
{"data":{"data1":"something"}}
All fields are optional when unmarshalling (you won't get an error if a struct field doesn't have an associated value in the JSON). When marshaling, you can use omitempty to not output a field if it contains its type's zero value:
https://pkg.go.dev/encoding/json#Marshal
var Test struct {
Data string `json:"data,omitempty" validate:"option"`
}

JSON decode specific field golang

I have this simple function, which gets a JSON response from a specific URI. The function accepts the httpRequest and an interface{}, which is a pointer to the struct in which I want to unmarshal my JSON response.
func (c *Client) SendRequest(req *http.Request, v interface{}) error {
...
return json.NewDecoder(resp.Body).Decode(&v)
}
An example of JSON response is:
{
"data": {
"id": "9da7a204-544e-5fd1-9a12-61176c5d4cd8"
}
}
An example of struct is:
type User struct {
ID string `json:"id;omitempty"`
}
Now, the problem is the data object. With this object, the Decode operation fails, as the object it is not included in my struct. I would like to Decode the content of data object directly, without using a temporary struct, but I don't understand how to do it.
Use a struct that contains a User struct to unmarshal into. See Response struct below.
type User struct {
ID string `json:"id;omitempty"`
}
type Response struct {
Data User `json:"data"`
}
After unmarshaling if r is your Response instance you can access your User by r.Data
Since you don't know the type at compile time you can use interface{} as the type of the field too.
type Response struct {
Data interface{} `json:"data"`
}
r := Response{
Data: &User{},
}
json.NewDecoder(resp.Body).Decode(&r)
And then get your user instance by
userNew, _ := r.Data.(*User)
P.S: You have a typo on your code at json tag. Replace ; by ,

How to unmarshall JSON in golang

I have trouble unmarshal access the values of a JSON string in my golang service.
I read the documentation for golang but the JSON objects in the examples are all differently formated.
from my api i get the following JSON string:
{"NewDepartment":
{
"newDepName":"Testabt",
"newDepCompany":2,
"newDepMail":"Bla#bla.org"
}
}
in go I defined the following data types:
type NewDepartment struct {
NewDepName string `json:"newDepName"`
NewDepCompany int `json:"newDepCompany"`
NewDepMail string `json:"newDepMail"`
}
type NewDeps struct {
NewDeps []NewDepartment `json:"NewDepartment"`
}
I try to unmarshal the JSON (from request Body) and access the values, but I can't get any results
var data types.NewDepartment
errDec := json.Unmarshal(reqBody, &data)
fmt.Println("AddDepartment JSON string got: " + data.NewDepName)
but it contains no string - nothing is displayed but no error on unmarshaling or Println.
Thanks for the help.
You're almost there.
First update is to make NewDeps.NewDeps a single object, not an array (according to the provided JSON).
The second update is to deserialize JSON into NewDeps, not into NewDepartment.
Working code:
type NewDepartment struct {
NewDepName string `json:"newDepName"`
NewDepCompany int `json:"newDepCompany"`
NewDepMail string `json:"newDepMail"`
}
type NewDeps struct {
NewDeps NewDepartment `json:"NewDepartment"`
}
func main() {
var data NewDeps
json.Unmarshal([]byte(body), &data)
fmt.Println("AddDepartment JSON string got: " + data.NewDeps.NewDepName)
}
https://play.golang.org/p/Sn02hwETRv1

Does json.Unmarshal require your result structure to match exactly the JSON passed in?

I have a JSON string I want to unmarshal:
{
"id":1720,
"alertId":1,
"alertName":"{stats} Test Lambda Alert",
"dashboardId":5,
"panelId":2,
"userId":0,
"newState":"alerting",
"prevState":"ok",
"time":1523983581000,
"text":"",
"regionId":0,
"tags":[],
"login":"",
"email":"",
"avatarUrl":"",
"data":{
"evalMatches":[
{
"metric":"{prod}{stats} Lambda Alert Test",
"tags":null,
"value":16.525333333333332
}
]
}
}
I get the raw stream via a request: bodyBytes, err := ioutil.ReadAll(resp.Body)
I was hoping I could just specify a struct that pulls the values I care about, e.g.,
type Result struct {
ID string `json:"id"`
Time int64 `json:"time"`
}
However, when I try this, I get errors.
type Result struct {
ID string `json:"id"`
Time string `json:"time"`
}
var result Result
err2 := json.Unmarshal(bodyBytes, &result)
if err2 != nil {
log.Fatal(fmt.Sprintf(`Error Unmarshalling: %s`, err2))
}
fmt.Println(result.ID)
Error Unmarshalling: json: cannot unmarshal array into Go value of type main.Result
I suspect this error may be due to what's actually returned from ioutil.ReadAll(), since it has the above JSON string wrapped in [ ] if I do a fmt.Println(string(bodyBytes)), but if I try to json.Unmarshal(bodyBytes[0], &result), I just get compile errors, so I'm not sure.
If I want to unmarshal a JSON string, do I have to specify the full structure in my type Result struct? Is there a way around this? I don't want to be bound to the JSON object I receive (if the API changes upstream, it requires us to modify our code to recognize that, etc.).
You can unmarshal into structs that represent only some fields of your JSON document, but the field types have to match, as the error clearly states:
cannot unmarshal number into Go struct field Result.id of type string
You cannot unmarshal a number into a string. If you define the ID field as any numeric type it'll work just fine:
package main
import (
"encoding/json"
"fmt"
)
var j = []byte(`
{
"id":1720,
"prevState":"ok",
"time":1523983581000,
"text":"",
"regionId":0
}
`)
type Result struct {
ID int `json:"id"` // or any other integer type, or float{32,64}, or json.Number
Time int64 `json:"time"`
}
func main() {
var r Result
err := json.Unmarshal(j, &r)
fmt.Println(r, err)
}
Try it on the playground: https://play.golang.org/p/lqsQwLW2dHZ
Update
You have just edited your question with the actual error you receive. You have to unmarshal JSON arrays into slices. So if the HTTP response in fact returns a JSON array, unmarshal into []Result:
var j = []byte(`
[
{
"id":1720,
"prevState":"ok",
"time":1523983581000,
"text":"",
"regionId":0
}
]
`)
var r []Result
err := json.Unmarshal(j, &r)
fmt.Println(r[0], err)
https://play.golang.org/p/EbOVA8CbcFO
To generate Go types that match your JSON document pretty well, use https://mholt.github.io/json-to-go/.

Json error calling MarshalJSON for type json.RawMessage

I am getting following error while trying to marshal this struct
json: error calling MarshalJSON for type json.RawMessage: unexpected
end of JSON input
for the object of below struct
type Chart struct {
ID int `json:"id,omitempty" db:"id"`
Name string `json:"name,omitempty" db:"name"`
Type string `json:"type,omitempty" db:"type"`
DashboardID int `json:"dashboard_id,omitempty"`
SourceType string `json:"source_type,omitempty" db:"source_type"`
Data json.RawMessage `json:"graph_data,ommitempty"`
}
func main() {
chart := Chart{}
chart.ID = 1
chart.Name = "Jishnu"
str, err := json.Marshal(chart)
fmt.Println(err)
}
Fixed by making Chart.Data a pointer
Data *json.RawMessage `json:"data,ommitempty"`
Go 1.8 (currently rc3 as of writing) will correctly handle Marshalling of both a pointer and non-pointer json.RawMessage.
Fixing commit: https://github.com/golang/go/commit/1625da24106b610f89ff7a67a11581df95f8e234