How to set optional json in nest struct - json

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"`
}

Related

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 decode a multi/single value in Json

I want to decode a request JSON that the value of a field could be a single string or array (list).
I know how to parse if separately. but I'm looking for a way to have a dynamic decoder to parse both of them.
The value field in the JSON is the case I'm talking about
Sample for single-value:
{
"filter":{
"op":"IN",
"field":"name",
"value": "testDuplicate"
}
}
Sample for multi-value:
{
"filter":{
"op":"IN",
"field":"name",
"value":["testDuplicate","tt"]
}
}
Structs for single value:
type DocumentTypeSearchRequest struct {
Filter DocTypeFilter `json:"filter"`
}
type DocTypeFilter struct {
Op string `json:"op"`
Field string `json:"field"`
Value string `json:"value"`
}
Structs for multi-value:
type DocumentTypeSearchRequest struct {
Filter DocTypeFilter `json:"filter"`
}
type DocTypeFilter struct {
Op string `json:"op"`
Field string `json:"field"`
Value []string `json:"value"`
}
One solution is to try to decode with one of them if failed use another one, but I'm not sure if that is the proper way to handle this problem.
You need a custom unmarshaler. I've talked about a similar situation in this video:
type Value []string
func (v *Value) UnmarshalJSON(p []byte) error {
if p[0] == '[' { // First char is '[', so it's a JSON array
s := make([]string, 0)
err := json.Unmarshal(p, &s)
*v = Value(s)
return err
}
// else it's a simple string
*v = make(Value, 1)
return json.Unmarshal(p, &(*v)[0])
}
Playground link
With this definition of Value, your struct would become something like:
type DocTypeFilter struct {
Op string `json:"op"`
Field string `json:"field"`
Value Value `json:"value"`
}
and now your Value field will always be a slice, but it may contain only a single value when your JSON input is a string, and not an array.

Retrieve element from nested JSON string

This is the Go code that I have:
func main(){
s := string(`{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`)
var result map[string]interface{}
json.Unmarshal([]byte(s), &result)
fmt.Println("Id:", result["Id"])
Rlist := result["RelatedItems"].([]map[string]interface{})
for key, pist := range pist {
fmt.Println("Key: ", key)
fmt.Println("RID:", pist["RId"])
}
}
The struct is down below
type Model struct {
Id string `json:"Id"`
Name string `json:"ModelName"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"PCId"`
RName string `json:"PCName"`
RChildren string `json:"string"`
}
How would I get an output that would let me choose a particular field from the above?
eg:
Output
Id: ABC123
key:0
RID:TEST123
key:1
RID:TEST234
I am seeing this error
panic: interface conversion: interface {} is nil, not []map[string]interface {}
Based on the posted content,
I'm clear that you are facing issues retrieving data from the nested JSON string.
I've taken your piece of code and tried to compile and reproduce the issue.
After observing, I have a few suggestions based on the way the code has been written.
When the datatype present in the s is known to be similar to the type Model, the result could have been declared as type Model.
That results in var result Model instead of map[string]interface{}.
When the data that's gonna be decoded from the interface{} is not known, the usage of switch would come into rescue without crashing the code.
Something similar to:
switch dataType := result["RelatedItems"].(type){
case interface{}:
// Handle interface{}
case []map[string]interface{}:
// Handle []map[string]interface{}
default:
fmt.Println("Unexpected-Datatype", dataType)
// Handle Accordingly
When we try to Unmarshal, we make sure to look into the json tags that are provided for the fields of a structure. If the data encoded is not having the tags we provided, the data will not be decoded accordingly.
Hence, the result of decoding the data from s into result would result in {ABC123 [{ } { }]} as the tags of the fields Name, RId, RName, RChildren are given as ModelName, PCId, PCName, string respectively.
By the above suggestions and refining the tags given, the piece of code would be as following which would definitely retrieve data from nested JSON structures.
s := string(`{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`)
var result Model
json.Unmarshal([]byte(s), &result)
fmt.Println(result)
type Model struct {
Id string `json:"Id"`
Name string `json:"Name"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"RId"`
RName string `json:"RName"`
RChildren string `json:"RChildren"`
}
This results in the output: {ABC123 Hello [{TEST123 TEST1 Ch1} {TEST234 TEST2 Ch2}]}
Why would you unmarshal to a map anyway and go through type checks?
type Model struct {
Id string `json:"Id"`
Name string `json:"ModelName"`
RelatedItems []RelatedItems `json:"RelatedItems"`
}
type RelatedItems struct {
RId string `json:"PCId"`
RName string `json:"PCName"`
RChildren string `json:"string"`
}
s := `{"Id": "ABC123",
"Name": "Hello",
"RelatedItems":[
{"RId":"TEST123","RName":"TEST1","RChildren":"Ch1"},
{"RId":"TEST234","RName":"TEST2","RChildren":"Ch2"}]
}`
var result Model
if err := json.Unmarshal([]byte(s), &result); err != nil {
log.Fatal(err.Error())
}
fmt.Println("Id: ", result.Id)
for index, ri := range result.RelatedItems {
fmt.Printf("Key: %d\n", index)
fmt.Printf("RID: %s\n", ri.RId)
}

JSON unmarshal in nested struct

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.

Golang json decoding fails on Array

I have an object like:
a = [{
"name": "rdj",
"place": "meh",
"meh" : ["bow", "blah"]
}]
I defined a struct like:
type first struct {
A []one
}
type one struct {
Place string `json: "place"`
Name string `json: "name"`
}
when I use the same in code like:
func main() {
res, _ := http.Get("http://127.0.0.1:8080/sample/")
defer res.Body.Close()
var some first
rd := json.NewDecoder(res.Body)
err := rd.Decode(&some)
errorme(err)
fmt.Printf("%+v\n", some)
}
I get the below error:
panic: json: cannot unmarshal array into Go value of type main.first
My understanding is:
type first defines the array and within that array is data structure defined in type one.
The JSON is an array of objects. Use these types:
type one struct { // Use struct for JSON object
Place string `json: "place"`
Name string `json: "name"`
}
...
var some []one // Use slice for JSON array
rd := json.NewDecoder(res.Body)
err := rd.Decode(&some)
The the types in the question match this JSON structure:
{"A": [{
"name": "rdj",
"place": "meh",
"meh" : ["bow", "blah"]
}]}
#rickydj,
If you need to have "first" as a separate type:
type first []one
If you do not care about having "first" as a separate type, just cut down to
var some []one
as #Mellow Marmot suggested above.