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 ,
Related
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"`
}
I'm currently trying to create the following nested json including a list of json and DER encoded byte array () of a certificate using golang:
{
"webhooks":
[{
"clientConfig":{
"caBundle":"<derData []bytes>"
},
"name":"sth_name"
}]
}
Because of <certDerBytes[]>, I need to use a struct, but I don't know how to initialize it.
I've created the struct so far:
type jsonstruct struct {
Webhooks []struct {
ClientConfig struct {
CaBundle string `json:"caBundle"`
} `json:"clientConfig"`
Name string `json:"name"`
} `json:"webhooks"`
}
But cannot instantiate the struct which I have to marshal into json.
I've tried using string literals, many ways of initializing it as you would for a normal non-nested struct.
I've also dividing up the structs i.e. type jsonstruct.. type webhooks ..., etc, but that errored.
I've also initialized the struct from the inside out, but didn't work either.
I need to create the
You're best off probably using base64 on the byte array itself, and include that as the payload of the struct field.
One piece of nit, I personally do not like nested named-structs. Breaking them out lets you have much more flexibility in your code.
For example:
type jsonstruct struct {
Webhooks []Webhook `json:"webhooks"`
}
type Webhook struct {
ClientConfig ClientConfig `json:"clientConfig"`
Name string `json:"name"`
}
type ClientConfig struct {
CaBundle string `json:"caBundle"`
}
func (cc ClientConfig) ToBytes() []byte {
return []byte(base64.StdEncoding.DecodeString(cc.CaBundle))
}
func (cc ClientConfig) FromBytes(cert []byte) {
cc.CaBundle = base64.StdEncoding.EncodeToString(cert)
}
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
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.
I am trying to unmarshal JSON into a struct. However, the struct has a field with a tag. Using reflection, and I try to see if the tag has the string "json" in it. If it does, then the json to unmarshal should simply be unmarshaled into the field as a string.
Example:
const data = `{"I":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S string `sql:"type:json"`
}
Problem is simple - unmarshal "S" in the json as a string into the struct A.
This is how far I have come. But I am stuck here.
http://play.golang.org/p/YzrhjuXxGN
This is the go way of doing it - no reflection requred. Create a new type RawString and create MarshalJSON and UnmarshalJSON methods for it. (playground)
// RawString is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawString string
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawString) MarshalJSON() ([]byte, error) {
return []byte(*m), nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawString) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
*m += RawString(data)
return nil
}
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S RawString `sql:"type:json"`
}
func main() {
a := A{}
err := json.Unmarshal([]byte(data), &a)
if err != nil {
log.Fatal("Unmarshal failed", err)
}
fmt.Println("Done", a)
}
I modified the implementation of RawMessage to create the above.
The problem here is that you are using one encoding format for two protocols and probably there is something wrong in your model.
That is a valid Json payload and should be handled as such. One trick that you can use is to create
another field to handle the "string" json and one to handle the "json struct".
See this modified example: I first unmarshal json and then marshal back to json to create the final string to upload to the database. One field is used for unmarshaling and the other to communicate with the DB.
package main
import(
"fmt"
"encoding/json"
)
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
Sjson struct {
Phone struct {
Sales string `json:"sales"`
} `json:"phone"`
} `json:"S", sql:"-"`
S string `sql:"type:json",json:"-"`
}
func main() {
msg := A{}
_ = json.Unmarshal([]byte(data), &msg)
data, _ := json.Marshal(msg.Sjson)
msg.S = string(data)
fmt.Println("Done", msg)
}
Looks like the problem is that s interface{} in your code was not addressable. For Value.SetString the Value has to be addressable and with Kind String. you can check the documentation for it - http://golang.org/pkg/reflect/#Value.SetString
How i understand it SetString would not change the value in a, since you are only working with interface s. in Laws of Reflection you can find "reflect.ValueOf is a copy of x, not x itself"(3rd Law).
To make your code work I made some type assertions, and I used reflect.ValueOf on a pointer to asserted struct.
To check if Value is settable or addressable you can use Value.CanSet ad Value.CanAddr
working code: http://play.golang.org/p/DTriENkzA8
No idea whether its correct way to do this