interface{}
is (legacy) go short-hand for "this could be anything". It does not represent an "object" (though it could). From go 1.18 onward the keyword any
was introduced as a direct replacement for interface{}
(though the latter may continue to be used if you need compatibility with older golang versions).
Here-on I shall use any
for brevity.
I'd suggest ignoring efficiency unless/until it becomes a problem you need to solve and instead focus on the clearest and simplest way to achieve what you need, functionally.
It is difficult to be clear about what exactly you are faced with and trying to achieve; your "example" object contains both quoted and unquoted field members so I see four possible scenarios that you may be dealing with:
- an
any
variable holding a value that is of a known formal struct type
- an
any
variable holding a value that is of an anonymous formal struct type
- an
any
variable holding a JSON string
- an
any
variable holding a map[string]any
For scenario's 1 and 2 that would be to marshal to JSON then unmarshal to map[string]any
.
For scenario 3 you would cast to string then unmarshal to map[string]any
:
For scenario 4 you would directly cast to map[string]any
.
I have worked up all of these in a playground for you here: https://go.dev/play/p/cSdUmynTFRp
package main
import (
"encoding/json"
"fmt"
)
type AnotherStruct struct {
X int `json:"x"`
}
type Foo struct {
A int `json:"a"`
B string `json:"b"`
C AnotherStruct `json:"c"`
}
func emit(m map[string]any) {
for k, v := range m {
fmt.Printf(" %s: %s\n", k, v)
}
fmt.Println()
}
func scenario1or2(n int, foo any) map[string]any {
fmt.Printf("scenario %d:\n", n)
j, _ := json.Marshal(foo)
m := map[string]any{}
json.Unmarshal(j, &m)
return m
}
func scenario3(foo any) map[string]any {
fmt.Println("scenario 3")
m := map[string]any{}
json.Unmarshal([]byte(foo.(string)), &m)
return m
}
func scenario4(foo any) map[string]any {
fmt.Println("scenario 4")
return foo.(map[string]any)
}
func main() {
emit(scenario1or2(1, Foo{
A: 1,
B: "test",
C: AnotherStruct{X: 42},
}))
emit(scenario1or2(2, struct {
A int
B string
C AnotherStruct
}{
A: 1,
B: "test",
C: AnotherStruct{
X: 42,
},
}))
emit(scenario3(`{"a":1,"b":"test","c":{"x":42}}`))
emit(scenario4(map[string]any{
"a": 1,
"b": "test",
"c": AnotherStruct{
X: 42,
},
}))
}
If you have scenario 1 and simply want efficient access to the fields (i.e. iterating over potentially unknown fields is not actually required) then you can type-cast directly to the known formal struct type:
// assuming...
type Foo struct {
a int
b string
c *AnotherStruct {
}
}
// and where 'anyfoo' is an `any` holding a Foo
foo := anyfoo.(Foo)
a := foo.a
b := foo.b
c := foo.c