让我们考虑以下代码
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"
}
现在,如果我尝试绑定A
类型的实例,如下所示
var bodyJSON A
ShouldBindWith(&bodyJson, binding.JSON)
我收到以下错误
json: cannot unmarshal object into Go struct field A.entity of type package.CustomInterface
我在做什么非常愚蠢的事吗?
PS:我刚开始探索去吧。如果这个问题非常高,那就道歉。答案 0 :(得分:0)
除了没有任何方法的空接口(json.Unmarshal
)之外,interface{}
函数本身不会让您解组为接口类型:
要将JSON解组为接口值,Unmarshal将one of these存储在接口值中:
bool
,适用于JSON布尔值float64
,代表JSON号码string
,用于JSON字符串[]interface{}
,适用于JSON数组map[string]interface{}
,用于JSON对象nil
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"`
}
如果我们知道某个实体是员工或客户,那么我们可以定义一个嵌入每个实体的Entity
:
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
}
虽然这与上述其他解决方案具有相同的缺点。