我有一些形式的JSON:
[{
"type": "car",
"color": "red",
"hp": 85,
"doors": 4
}, {
"type": "plane",
"color": "blue",
"engines": 3
}]
Run Code Online (Sandbox Code Playgroud)
我有类型car和plane满足车辆接口; 我想能够写:
var v []vehicle
e := json.Unmarshal(myJSON, &v)
Run Code Online (Sandbox Code Playgroud)
...让JSON用汽车和飞机填满我的车辆; 相反(并且不出所料)我只是得到"不能将对象解组为类型为main.vehicle的Go值".
供参考,以下是所涉及类型的合适定义:
type vehicle interface {
vehicle()
}
type car struct {
Type string
Color string
HP int
Doors int
}
func (car) vehicle() { return }
type plane struct {
Type string
Color string
Engines int
}
func (plane) vehicle() { return }
var _ vehicle = (*car)(nil)
var _ vehicle = (*plane)(nil)
Run Code Online (Sandbox Code Playgroud)
(请注意,我其实是在完全不感兴趣t的领域car和plane-它可以被省略,因为这些信息会,如果有人成功地回答了这个问题,在动态类型的对象的隐v).
有没有办法让JSON umarhsaller根据被解码数据的某些部分内容(在本例中为类型字段)选择使用哪种类型?
(请注意,这不是Unmarshal JSON与未知字段的重复,因为我希望切片中的每个项目具有不同的动态类型,并且从'type'属性的值我确切知道要期望的字段 - 我只是不要我不知道如何告诉json.Unmarshal如何将'type'属性值映射到Go类型.)
从类似的问题中解答:用未知字段解组JSON,我们可以构造一些方法来解析[]vehicle数据结构中的这个JSON对象.
可以使用通用[]map[string]interface{}数据结构,然后vehicles从地图切片构建正确的"手动处理Unmarshal"版本.为简洁起见,此示例确实省略了json包将丢失或错误键入的字段的错误检查.
https://play.golang.org/p/fAY9JwVp-4
func NewVehicle(m map[string]interface{}) vehicle {
switch m["type"].(string) {
case "car":
return NewCar(m)
case "plane":
return NewPlane(m)
}
return nil
}
func NewCar(m map[string]interface{}) *car {
return &car{
Type: m["type"].(string),
Color: m["color"].(string),
HP: int(m["hp"].(float64)),
Doors: int(m["doors"].(float64)),
}
}
func NewPlane(m map[string]interface{}) *plane {
return &plane{
Type: m["type"].(string),
Color: m["color"].(string),
Engines: int(m["engines"].(float64)),
}
}
func main() {
var vehicles []vehicle
objs := []map[string]interface{}{}
err := json.Unmarshal(js, &objs)
if err != nil {
log.Fatal(err)
}
for _, obj := range objs {
vehicles = append(vehicles, NewVehicle(obj))
}
fmt.Printf("%#v\n", vehicles)
}
Run Code Online (Sandbox Code Playgroud)
我们可以再次利用json包来处理单个结构的解组和类型检查,方法是将第二次直接解组为正确的类型.json.Unmarshaler通过UnmarshalJSON在[]vehicle类型上定义一个方法,首先将JSON对象拆分为原始消息,这可以全部包含在实现中.
https://play.golang.org/p/zQyL0JeB3b
type Vehicles []vehicle
func (v *Vehicles) UnmarshalJSON(data []byte) error {
// this just splits up the JSON array into the raw JSON for each object
var raw []json.RawMessage
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
for _, r := range raw {
// unamrshal into a map to check the "type" field
var obj map[string]interface{}
err := json.Unmarshal(r, &obj)
if err != nil {
return err
}
vehicleType := ""
if t, ok := obj["type"].(string); ok {
vehicleType = t
}
// unmarshal again into the correct type
var actual vehicle
switch vehicleType {
case "car":
actual = &car{}
case "plane":
actual = &plane{}
}
err = json.Unmarshal(r, actual)
if err != nil {
return err
}
*v = append(*v, actual)
}
return nil
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4128 次 |
| 最近记录: |