在Go中解析JSON数组的数组

17
我想在Go语言中解析一些JSON数据。数据的格式如下:
{
    "id": "someId",
    "key_1": "value_1",
    "key_2": "value_2",
    "key_3": "value_3",
    "points": [
        [
            1487100466412,
            "50.032178",
            "8.526018",
            300,
            0.0,
            26,
            0
        ],
        [
            1487100471563,
            "50.030869",
            "8.525949",
            300,
            0.0,
            38,
            0
        ],
        [
            1487100475722,
            "50.028514",
            "8.525959",
            225,
            0.0,
            69,
            -900
        ],
        [
            1487100480834,
            "50.025827",
            "8.525793",
            275,
            0.0,
            92,
            -262
        ]
    ]
}

我建立了一个go结构体:
type SomeStruct struct {
   ID   string `json:"id"`
   Key1 string `json:"key_1"`
   Key2 string `json:"key_2"`
   Key3 string `json:"key_3"`
   Points []Point `json:"points"`
}

type Point struct {
   Timestamp int64 `json:"0"`
   Latitude float64 `json:"1,string"`
   Longitude float64 `json:"2,string"`
   Altitude int `json:"3"` 
   Value1 float64 `json:"4"`
   Value2 int `json:"5"`
   Value3 int `json:"6"`      
}

我解析JSON数据
var track SomeStruct
error := json.Unmarshal(data,&track)
if(error != nil){
    fmt.Printf("Error while parsing data: %s", error)
}

json: 无法将数组解组为类型为Point{someId value_1 value_2 value_3 [{0 0 0 0 0 0 0} {0 0 0 0 0 0 0} {0 0 0 0 0 0 0}...]}的Go值。
所以第一个json键被正确解析,但我无法弄清楚如何获取点数据,这是一个数组的数组。
生成的结构也是从这里建议的,除了我不使用嵌套结构而是使用单独的类型。使用建议的嵌套结构没有任何区别:JSON-to-Go
我需要为此实现自己的解组器吗?
======= 更新解决方案 ============
只需为Point结构实现UnmarshalJSON接口即可。 下面的示例没有包含适当的错误处理,但它展示了方向。Playground example
package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

type SomeStruct struct {
    ID     string  `json:"id"`
    Key1   string  `json:"key_1"`
    Key2   string  `json:"key_2"`
    Key3   string  `json:"key_3"`
    Points []Point `json:"points"`
}

type Point struct {
    Timestamp int64
    Latitude  float64
    Longitude float64
    Altitude  int
    Value1    float64
    Value2    int16
    Value3    int16
}

func (tp *Point) UnmarshalJSON(data []byte) error {
    var v []interface{}
    if err := json.Unmarshal(data, &v); err != nil {
        fmt.Printf("Error whilde decoding %v\n", err)
        return err
    }
    tp.Timestamp = int64(v[0].(float64))
    tp.Latitude, _ = strconv.ParseFloat(v[1].(string), 64)
    tp.Longitude, _ = strconv.ParseFloat(v[2].(string), 64)
    tp.Altitude = int(v[3].(float64))
    tp.Value1 = v[4].(float64)
    tp.Value2 = int16(v[5].(float64))
    tp.Value3 = int16(v[6].(float64))

    return nil
}

func main() {

    const data = `{"id":"someId","key_1":"value_1","key_2":"value_2","key_3":"value_3","points":[[1487100466412,"50.032178","8.526018",300,0.0,26,0],[1487100471563,"50.030869","8.525949",300,0.0,38,0],[1487100475722,"50.028514","8.525959",225,0.0,69,-900],[1487100480834,"50.025827","8.525793",275,0.0,92,-262]]}`

    var something SomeStruct
    json.Unmarshal([]byte(data), &something)

    fmt.Printf("%v", something)
}

可能是重复的问题:如何解组具有不同类型值的JSON数组 - RayfenWindspear
感谢您将[]Point \json:"points"`替换为Points interface{}`,这至少解析了数据,这对我来说很有意义。 但是,我如何获得一个类型化的值而不是空接口呢? - chrisdo
通过使用类型断言:https://dev59.com/AGEi5IYBdhLWcg3w0O-Q - bvpx
你希望它是 [][]interface{} 类型,然后你就可以索引单个值了。 - RayfenWindspear
哎呀,不用理会我之前的评论了,我去玩了一下,发现我原以为能行的方法并不起作用。 - favoretti
@chrisdo 解决方案应该发布为自我回答,而不是作为问题的编辑。请参阅此元帖。谢谢。 - ggorlen
2个回答

12

JSON:

[
  {
    "type": "Car",
    "wheels": 4
  },
  {
    "type": "Motorcycle",
    "wheels": 2
  }
]

结构体:

type Vehicle struct {
  Type   string
  Wheels int
}

解码器(Unmarshaller):
func TestVehicleUnmarshal(t *testing.T) {
    response := `[{"type": "Car","wheels": 4},{"type": "Motorcycle","wheels": 2}]`

    var vehicles []Vehicle
    json.Unmarshal([]byte(response), &vehicles)

    assert.IsType(t, Vehicle{}, vehicles[0])
    assert.EqualValues(t, "Car", vehicles[0].Type)
    assert.EqualValues(t, 4, vehicles[0].Wheels)
    assert.EqualValues(t, "Motorcycle", vehicles[1].Type)
    assert.EqualValues(t, 2, vehicles[1].Wheels)
}

https://play.golang.org/p/5SfDH-XZt9J


你的例子真的很有帮助!! - Arun
我总是忘记我可以对结构体进行切片! 谢谢! - james-see

3

我需要为此实现自己的Unmarshaller吗?

是的。

你试图将一个数组反序列化成一个结构体(Point),这意味着你需要告诉JSON反序列化器如何将数组值映射到结构体值上。

还要注意,你在 Point 定义中的标记是不正确的。json标记指的是键名,但是数组没有键(在JavaScript中,它们可以像有键一样访问,但这不是JavaScript)。换句话说,json:"0" 只有在JSON看起来像 {"0":123} 这样时才有效。如果你实现自己的Unmarshaler,你可以直接删除那些json标记。


只是一点提醒。只需要编写一个用于数组本身的非解组器。该数组将解组成点的 [][]interface{}。这部分需要手动处理。 - RayfenWindspear
非常感谢,我会尝试使用这个。我已经成功使用类型断言从Points中获取值,但是对数组使用自定义解码器听起来更加清晰明了。我一旦正确操作了就会更新帖子。 - chrisdo
@RayfenWindspear:我认为正确的方法是将指针数据解封到[]interface{}。标准的JSON编组器已经处理了[]Point部分。 - Jonathan Hall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接