如何从JSON将可变深度数组解析为结构

时间:2019-05-15 02:31:18

标签: go

我正在将JSON从API解析为结构。我代表该结构的代码无法正常工作,我注意到这是因为API返回的是可变深度数组(例如,有时[] float64,有时[] [] float64)。我不知道如何设计一个结构来处理这个问题。

我已经尝试通过在[] float64,[] [] float64和[] interface {}之间交换有问题的类型来弄乱我的结构定义。

我的结构定义代码:

...
Geometries []struct {
    Type        string    `json:"type"`
    Coordinates []float64 `json:"coordinates"`
} `json:"geometries"`
...

以下是API的示例输出:

...
"geometries": [{"type": "Point", "coordinates": [-81.7046006, 41.4955689]}, {"type": "Polygon", "coordinates": [[[-81.7176885, 41.507513], [-81.7205424, 41.4857622], [-81.6915158, 41.4836231], [-81.6886557, 41.5053737], [-81.7176885, 41.507513]]]}]
...

我希望JSON能够解析到该结构中。相反,我在代码中看到以下错误: json: cannot unmarshal array into Go struct field .coordinates of type float64

1 个答案:

答案 0 :(得分:2)

使用json.RawMessage捕获随几何类型而变化的值:

Geometries []struct {
    Type        string          `json:"type"`
    Coordinates json.RawMessage `json:"coordinates"`
} `json:"geometries"`

将坐标解组为适合每种类型的值:

for _, geo := range x.Geometries {
    switch geo.Type {
    case "Point":
        var coords []float64
        if err := json.Unmarshal(geo.Coordinates, &coords); err != nil {
            log.Fatal(err)
        }
        fmt.Println(coords)
    case "Polygon":
        var coords [][][]float64
        if err := json.Unmarshal(geo.Coordinates, &coords); err != nil {
            log.Fatal(err)
        }
        fmt.Println(coords)
    }
}

Run it on the playground

另一种选择是解组到interface{}并使用type assertions来挖掘值:

Geometries []struct {
    Type        string      `json:"type"`
    Coordinates interface{} `json:"coordinates"`
} `json:"geometries"`

...

for _, geo := range x.Geometries {
    fmt.Println(geo.Type)
    dump(geo.Coordinates, "")
}

...

func dump(v interface{}, indent string) {
    switch v := v.(type) {
    case []interface{}:
        fmt.Println(indent, "[")
        for _, v := range v {
            dump(v, indent+"  ")
        }
        fmt.Println(indent, "]")
    default:
        fmt.Println(indent, v)
    }
}

Run it on the playground