I have a very interesting case, let's say we have this struct
type Test struct {
Field1 string `json:"field1"`
Field2 ABC `json:"abc"`
}
type ABC interface {
Rest()
}
Unmarshalling this struct is not a problem, you could just point to the right struct which implements the interface, unless you have []Test
Is there a way to unmarshall slice of structs when one of the field is interface?
Thanks
CodePudding user response:
You need to implement Unmarshaler interface
to Test,
Then in UnmarshalJSON
func you need to check it one by one (line 45-55 in the example)
Luckily there is now generic
, this is the example :
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type Test struct {
Field1 string `json:"field1"`
Field2 ABC `json:"abc"`
}
type ABCImplements interface {
A | B
}
func UnmarshalABC[T ABCImplements](b []byte) (T, error) {
var x T
err := json.Unmarshal(b, &x)
if err != nil {
return x, err
}
rv := reflect.ValueOf(x)
if rv.IsZero() {
return x, fmt.Errorf("T is zero value")
}
return x, nil
}
func (m *Test) UnmarshalJSON(data []byte) error {
temp := make(map[string]interface{}, 2)
err := json.Unmarshal(data, &temp)
if err != nil {
return err
}
m.Field1 = temp["field1"].(string)
b, err := json.Marshal(temp["abc"])
if err != nil {
return err
}
xB, err := UnmarshalABC[B](b)
if err == nil {
m.Field2 = xB
return nil
}
xA, err := UnmarshalABC[A](b)
if err == nil {
m.Field2 = &xA
return nil
}
return nil
}
type A struct {
B string `json:"b"`
}
func (a *A) Rest() {
fmt.Println(a.B)
}
type B struct {
C string `json:"c"`
}
func (b B) Rest() {
fmt.Println(b.C)
}
type ABC interface {
Rest()
}
func main() {
a := &A{"coba"}
t := Test{"oke", a}
arrT := []Test{t, t, t}
b, err := json.Marshal(arrT)
if err != nil {
panic(err)
}
var xT []Test
err = json.Unmarshal(b, &xT)
fmt.Printf("%#v\n", xT)
fmt.Println(xT[1].Field2)
}
CodePudding user response:
Use the following code to Unmarshal the interface field to a specific concrete type. See the commentary for more details:
// Example is the concrete type.
type Example struct{ Hello string }
func (e *Example) Rest() {}
// Implement the json.Unmarshaler interface to control how
// values of type Test are unmarsheled.
func (m *Test) UnmarshalJSON(data []byte) error {
// Setup fields as needed.
m.Field2 = &Example{}
// We cannot call json.Unmarshal(data, m) to do the work
// because the json.Unmarshal will recurse back to this
// function. To prevent the recursion, we declare a new
// type with the same field layout as Test, but no methods:
type t Test
// Convert receiver m to a t* and unmarshal using that pointer.
v := (*t)(m)
return json.Unmarshal(data, v)
}