Golang Interface to struct conversion giving error

Issue

I have an json as a string of following format:

{"add": [{"var": ["100"]}, "200"]}

Here the key ‘add’ is not a constant value for all the jsons. In some cases, it can be ‘minus’, ‘multiply’ etc.
The value of that key is an array. In this case [{"var": ["100"]}, "200"]. This means that the 100 should be added to existing value 200.

I am trying to parse this expression. Since the main key(in this case ‘add’) is not a constant, I cannot convert into a struct. So, I converted it to a json object by following way:

type mathExpVar struct {
    valueVar []string `json:"var"`
}
var mathExpJson map[string][]interface{}
var input = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
err := json.Unmarshal([]byte(input), &mathExpJson)
for operator, values := range mathExpJson{
    vals, ok := values[0].(mathExpVar) // here values[0] will be {"var": ["100"]}
    if !ok{
        return nil
    }
}

Here ‘ok’ is always returning false. I am not sure why. There is no additional error message for me to check why this is failing. Could someone help me in resolving this?

Link to go playground for the same: https://go.dev/play/p/POfQmEoPbjD

Solution

Here the entire structure of the parsed json value will be stored in nested map[string]interface{}(json object) or []interface{}(json array) types.

In the line:

vals, ok := values[0].(mathExpVar)

values[0] would be of type map[string]interface{}, which cannot be asserted to mathExpVar, which is a struct, an entirely different datatype.

You need to type assert to map[string]interface{} first, then do this in each nested level as you go forward:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    type mathExpVar struct {
        valueVar []string `json:"var"`
    }
    var mathExpJson map[string][]interface{}
    var input = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
    err := json.Unmarshal([]byte(input), &mathExpJson)
    if err != nil {
        fmt.Println("Error in unmarshalling")
    }
    for _, values := range mathExpJson {
        var vals mathExpVar
        valMap, ok := values[0].(map[string]interface{})
        if ok {
            varSlice, ok := valMap["var"].([]interface{})
            if ok {
                for _, v := range varSlice {
                    nv, ok := v.(string)
                    if ok {
                        vals.valueVar = append(vals.valueVar, nv)
                    } else {
                        fmt.Printf("%T\n", v)
                    }
                }
            } else {
                fmt.Printf("%T\n", valMap["var"])
            }
        } else {
            fmt.Printf("%T\n", values[0])
        }
        fmt.Printf("%+v\n", vals)
    }
}

See: https://go.dev/play/p/Ot_9IZr4pwM

For more on interfaces and go reflection, check out: https://go.dev/blog/laws-of-reflection

Answered By – philoj

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.