让我们考虑下面的代码
type A struct {
Column1 string `json:"column1"`
Entity CustomInterface `json:"entity"`
}
type CustomInterface interface {
GetType() string
}
type Entity1 struct {
ColumnX string `json:"columnx"`
ColumnY string `json:"columny"`
}
type Entity2 struct {
ColumnP string `json:"columnp"`
ColumnQ string `json:"columnq"`
}
func (*e Entity1) GetType() string {
return "ENTITY1"
}
func (*e Entity2) GetType() string {
return "ENTITY2"
}
现在,如果我尝试按如下方式绑定(bind) A
类型的实例
var bodyJSON A
ShouldBindWith(&bodyJson, binding.JSON)
我收到以下错误
json: cannot unmarshal object into Go struct field A.entity of type package.CustomInterface
我是不是在做什么傻事?
PS:我刚刚开始探索围棋。如果这个问题非常菜鸟级别,我们深表歉意。
最佳答案
json.Unmarshal
函数本身不允许您解码为接口(interface)类型,但没有任何方法的空接口(interface) (interface{}
) 除外:
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:
bool
, for JSON booleansfloat64
, for JSON numbersstring
, for JSON strings[]interface{}
, for JSON arraysmap[string]interface{}
, for JSON objectsnil
for JSON null
然而,在一些简单的情况下,下面的方案可以工作。
type CustomerEntity struct {
CustomerName string `json:"customer_name"`
Address string `json:"customer_address"`
}
type EmployeeEntity struct {
EmployeeName string `json:"employee_name"`
ID int `json:"employee_id"`
}
如果我们知道一个实体是雇员或客户,那么我们可以定义一个嵌入每个实体的实体
:
type Entity struct {
CustomerEntity
EmployeeEntity
}
我们可以给它方法来检查它是客户还是员工:
func (s Entity) IsCustomer() bool {
return s.CustomerEntity != CustomerEntity{}
}
func (s Entity) IsEmployee() bool {
return s.EmployeeEntity != EmployeeEntity{}
}
实际上,这些只是检查是否至少设置了一个字段。
然后我们解码以下 JSON:
{
"entity": {
"employee_name": "Bob",
"employee_id": 77
}
}
这是一个完整的例子:
import (
"encoding/json"
"fmt"
)
type Example struct {
Entity Entity `json:"entity"`
}
type Entity struct {
CustomerEntity
EmployeeEntity
}
func (s Entity) IsCustomer() bool {
return s.CustomerEntity != CustomerEntity{}
}
func (s Entity) IsEmployee() bool {
return s.EmployeeEntity != EmployeeEntity{}
}
type CustomerEntity struct {
CustomerName string `json:"customer_name"`
CustomerAddress string `json:"customer_address"`
}
type EmployeeEntity struct {
EmployeeName string `json:"employee_name"`
EmployeeID int `json:"employee_id"`
}
func main() {
var example Example
if err := json.Unmarshal([]byte(`{"entity":{"employee_name":"Bob", "employee_id":77}}`), &example); err != nil {
panic("won't fail")
}
fmt.Printf("%#v\n", example)
if example.Entity.IsCustomer() {
fmt.Printf("customer %s lives at %d\n", example.Entity.CustomerName, example.Entity.CustomerAddress)
}
if example.Entity.IsEmployee() {
fmt.Printf("employee %s has id %d\n", example.Entity.EmployeeName, example.Entity.EmployeeID)
}
}
哪些输出
main.Example{Entity:main.Entity{CustomerEntity:main.CustomerEntity{CustomerName:"", CustomerAddress:""}, EmployeeEntity:main.EmployeeEntity{EmployeeName:"Bob", EmployeeID:77}}}
employee Bob has id 77
如我们所料。
有一些注意事项。首先,如果实体类型的 JSON 或 Go 字段名称存在重叠,这将不起作用。其次,没有什么能阻止您(意外地)初始化客户和员工类型中的某些字段并导致它为 IsCustomer
和 IsEmployee
返回 true。
如果您的 JSON 数据有一个 "type"
字段,那么您可以使用它来决定保留什么:
type Entity struct {
Type string `json:"type"`
CustomerEntity
EmployeeEntity
}
尽管这与上述其他解决方案具有相同的缺点。
关于json - 将 json 请求主体解码为具有自定义接口(interface)类型的结构成员的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48643718/