在这个例子中,我将尝试加载包含多边形的2D场景。在代码中,我会有许多不同的结构,如圆形,方形,矩形,五角形等。 所有人都会分享常见的 funcs ,例如Area和Perimeter。 场景本身将存储为多边形界面的切片。
以下是我用来测试此代码的代码:
package main
import (
"encoding/json"
"fmt"
"math"
)
type Polygon interface {
Area() float32
}
type Rectangle struct {
Base float32 `json:"base"`
Height float32 `json:"height"`
X float32 `json:"x"`
Y float32 `json:"y"`
}
func (r *Rectangle) Area() float32 {
return r.Base * r.Height
}
type Circle struct {
Radius float32 `json:"radius"`
X float32 `json:"x"`
Y float32 `json:"y"`
}
func (c *Circle) Area() float32 {
return c.Radius * c.Radius * math.Pi
}
func main() {
rect := Rectangle{Base: 10, Height: 10, X: 10, Y: 10}
circ := Circle{Radius: 10, X: 0, Y: 0}
sliceOfPolygons := make([]Polygon, 0, 2)
sliceOfPolygons = append(sliceOfPolygons, &rect, &circ)
jsonData, err := json.Marshal(sliceOfPolygons)
if err != nil {
panic(err)
}
fmt.Println(string(jsonData))
newSlice := make([]Polygon, 0)
err = json.Unmarshal(jsonData, &newSlice)
if err != nil {
panic(err)
}
}
在这个例子中,我设置了一个2个多边形的切片,编组它然后再尝试解组它。 编组的字符串是:
[{"base":10,"height":10,"x":10,"y":10},{"radius":10,"x":0,"y":0}]
但是,当我试图Unmarshal
恐慌时:
panic: json: cannot unmarshal object into Go value of type main.Polygon
如果这样做,它将非常有用且易于使用。我要说Unmarshall
无法区分json字符串中的Rectangle
和Circle
,因此无法知道要构建的结构。
有没有办法标记结构或告诉Unmarshal
如何区分这些结构?
答案 0 :(得分:1)
以这种方式来区分json是Circle还是Rectangle。在您的JSON中,没有可以检测对象类型的结构的标识。所以,让我们制定规则。
要解组JSON,它应该具有如下所示的常用字段。
type Object struct {
Base float32 `json:"base,omitempty"`
Radius float32 `json:"radius,omitempty"`
Height float32 `json:"height,omitempty"`
X float32 `json:"x"`
Y float32 `json:"y"`
}
这个结构可以存储Rectangle或Circle。然后,添加方法IsCircle和IsRectangle。
func (obj *Object) IsCircle() bool {
return obj.Radius > 0
}
func (obj *Object) IsRectangle() bool {
return obj.Base > 0 && obj.Height > 0
}
您可以使类似Kind()的方法返回struct的标识。你认为最好。最后,您应该添加ToCircle / ToRectangle方法。
func (obj *Object) ToCircle() *Circle {
return &Circle{
Radius: obj.Radius,
X: obj.X,
Y: obj.Y,
}
}
func (obj *Object) ToRectangle() *Rectangle {
return &Rectangle{
Base: obj.Base,
Height: obj.Height,
X: obj.X,
Y: obj.Y,
}
}
如果您想要切割多边形界面,则应将此切片的对象转换为多边形切片,如下所示。
var polygons []Polygon
for _, obj := range newSlice {
if obj.IsCircle() {
polygons = append(polygons, obj.ToCircle())
} else if obj.IsRectangle() {
polygons = append(polygons, obj.ToRectangle())
}
}
https://play.golang.org/p/kO_F4GTYdA
另一种方法。制作转换为map [string] interface {}的转换器。转换器可以检测具有查找字段的结构。
var converters = []func(map[string]interface{}) Polygon{
func(m map[string]interface{}) Polygon {
rectangle := new(Rectangle)
if base, ok := m["base"]; ok {
rectangle.Base = toFloat32(base)
} else {
return nil
}
if height, ok := m["height"]; ok {
rectangle.Height = toFloat32(height)
} else {
return nil
}
if x, ok := m["x"]; ok {
rectangle.X = toFloat32(x)
}
if y, ok := m["y"]; ok {
rectangle.Y = toFloat32(y)
}
return rectangle
},
func(m map[string]interface{}) Polygon {
circle := new(Circle)
if radius, ok := m["radius"]; ok {
circle.Radius = toFloat32(radius)
} else {
return nil
}
if x, ok := m["x"]; ok {
circle.X = toFloat32(x)
}
if y, ok := m["y"]; ok {
circle.Y = toFloat32(y)
}
return circle
},
}
并转换
var polygons []Polygon
for _, obj := range newSlice {
m, ok := obj.(map[string]interface{})
if !ok {
panic("invalid struct")
}
var p Polygon
for _, converter := range converters {
p = converter(m)
if p != nil {
break
}
}
if p == nil {
panic("unknown polygon")
}
polygons = append(polygons, p)
}