У меня есть общий объект Response со следующей структурой:
type Response struct {
Data Data `json:"data"`
Error string `json:"error,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}
Тип Data
— это интерфейс со многими реализациями (например, PingResponse и т. д.). Как мне преобразовать Response
в его базовый тип? Ниже приведен полный пример, который всегда вызывает ошибку error: json: cannot unmarshal object into Go struct field Response.data of type main.Data
:
type Response struct {
Data Data `json:"data"`
Error string `json:"error,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}
type Data interface{
Foo()
}
type TypeA struct {
Field1 string `json:"field1"`
Field2 int `json:"field2"`
}
func (a *TypeA) Foo() {}
type TypeB struct {
Field3 float64 `json:"field3"`
}
func (b *TypeB) Foo() {}
func main() {
jsonStr := `{
"data": {
"field1": "some string",
"field2": 123
},
"error": "",
"next_page_token": ""
}`
var response Response
err := json.Unmarshal([]byte(jsonStr), &response)
if err != nil {
fmt.Println("error:", err)
return
}
switch data := response.Data.(type) {
case *TypeA:
fmt.Println("TypeA:", data.Field1, data.Field2)
case *TypeB:
fmt.Println("TypeB:", data.Field3)
default:
fmt.Println("Unknown type")
}
}
Вы должны сказать encoding/json
, какой конкретный тип немаршалировать. Пакет не может сделать это за вас.
Предположим, что TypeA
и TypeB
определены как:
type TypeA struct {
FieldA string `json:"field"`
}
type TypeB struct {
FieldB string `json:"field"`
}
В этом случае невозможно решить, какой тип демаршалировать.
Что касается вашего примера, мы можем сказать encoding/json
, какой тип неупорядочить, чтобы это понравилось:
- var response Response
+ response := Response{Data: &TypeA{}}
Если вы не знаете тип заранее, вы можете маршалировать его в map[string]interface{}
:
type Response struct {
- Data Data `json:"data"`
+ Data map[string]interface{} `json:"data"`
Error string `json:"error,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}
И определить тип следующим образом:
if field1, ok := response.Data["field1"]; ok {
fmt.Println("TypeA:", field1, response.Data["field2"])
} else {
if field3, ok := response.Data["field3"]; ok {
fmt.Println("TypeB:", field3)
} else {
fmt.Println("Unknown type")
}
}
Другое решение — встроить информацию о типе в json:
jsonStr := `{
"data": {
"field1": "some string",
"field2": 123
},
+ type": "A",
"error": "",
"next_page_token": ""
}`
type Response struct {
- Data Data `json:"data"`
+ Data json.RawMessage `json:"data"`
+ Type string `json:"type"`
Error string `json:"error,omitempty"`
NextPageToken string `json:"next_page_token,omitempty"`
}
А затем расшифровать response.Data
в соответствии со значением response.Type
. См. пример, предоставленный encoding/json
: https://pkg.go.dev/encoding/json#example-RawMessage-Unmarshal.
если вам нужно проверить данные, чтобы определить, как правильно их преобразовать, вам может пригодиться pkg.go.dev/github.com/mitchellh/mapstructure