Golang Interface to struct conversion giving error

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 1:

A Working example: https://go.dev/play/p/02YzI5cv8vV

The whole reason the original response from Burak Serdar was not valid in my opinion is that it does not take into account the fact that you would need to handle the rest of the params as well. If you look closely enough, then you see that the expression is not an array of strings, its of varying type. This implementation handles the custom Unmarshalling and stores all the extra parameters in the Extra field.

Also code:

package main

import (
    "encoding/json"
    "log"
    "reflect"
)

const jsonPayload = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"

func main() {

    data := MathExpressions{}
    err := json.Unmarshal([]byte(jsonPayload), &data)

    if err != nil {
        log.Println("Failed to unmarshal json, error:", err)
        return
    }

    log.Println(data)

    for operation, expression := range data {
        log.Print("Op:", operation, "Exp:", expression)
    }

    log.Println("Finished..")
}

/**
The sub definition for a specific expression in the object
*/
type ExpressionDefinition struct {
    Vars  []string `json:"var"`
    Extra []string
}

func (e *ExpressionDefinition) UnmarshalJSON(data []byte) error {
    tokens := make([]interface{}, 0)
    err := json.Unmarshal(data, &tokens)

    if err != nil {
        return err
    }

    for _, token := range tokens {
        log.Println("Processing token:", token, "type:", reflect.TypeOf(token))
        switch token.(type) {
        case map[string]interface{}:
            for _, v := range token.(map[string]interface{})["var"].([]interface{}) {
                e.Vars = append(e.Vars, v.(string))
            }
        case string:
            e.Extra = append(e.Extra, token.(string))
        }
    }

    log.Println(tokens)
    return nil
}

/**
The main expressions object which contains all the sub-expressions.
*/
type MathExpressions map[string]ExpressionDefinition