Another solution is to unmarshal the varying elements of your JSON arrray into values of types you expect, and process / create an Obj wrapper for the string values should you need it.
Since your input is a JSON array, it can be unmarshalled into an array or slice. And since the elements are of different types, the element type of the Go array or slice must be interface{}.
Unmarshaling into a value of type []interface{} would result in the encoding/json package choosing map[string]interface{} for the object elements. But if you populate the target slice prior with elements of the types you wish to unmarshal into, the json package will obediently use those:
func main() {
res := []interface{}{
&Obj{},
"",
&Obj{},
}
if err := json.Unmarshal([]byte(src), &res); err != nil {
panic(err)
}
for _, v := range res {
fmt.Printf("type = %-10T value = %+q\n", v, v)
}
}
const src = `[
{"name": "obj1", "key2": ["a", "b"]},
"obj2",
{"name": "obj3"}
]`
Output (try it on the Go Playground):
type = *main.Obj value = &{"obj1" ["a" "b"]}
type = string value = "obj2"
type = *main.Obj value = &{"obj3" []}