json.Unmarshal 函数本身不允许您解组接口类型,除了没有任何方法的空接口 (interface{}):
为了将 JSON 解组为接口值,Unmarshal 将 one of these 存储在接口值中:
-
bool,用于 JSON 布尔值
-
float64,用于 JSON 数字
-
string,用于 JSON 字符串
-
[]interface{},用于 JSON 数组
-
map[string]interface{},用于 JSON 对象
-
nil 为 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
}
尽管这与上述其他解决方案具有相同的缺点。