Unmarshal JSON Array of arrays in Go

huangapple go评论84阅读模式
英文:

Unmarshal JSON Array of arrays in Go

问题

我想在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
err := json.Unmarshal(data, &track)
if err != nil {
    fmt.Printf("Error while parsing data: %s", err)
}

但是我无法获取点数据,它是一个数组的数组。

生成的结构体也是从这里建议的,除了我没有使用嵌套结构体,而是使用了一个单独的类型。使用建议的嵌套结构体没有任何区别。

我需要为此实现自己的解析器吗?

======= 更新解决方案 ============

只需为Point结构体实现UnmarshalJSON接口即可。下面的示例没有包含适当的错误处理,但它展示了方向。

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 while 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-to-Go

我需要为此实现自己的解析器吗?

英文:

I want to parse some json data in go. The data looks like this:

> {"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],...]}

I built a go struct:

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"`      
}

I unmarshal the json data

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

> json: cannot unmarshal array into Go value of type 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}...]}

So the first json keys are parsed correctly, but I cannot figure out how to get the point data, which is an array of arrays.

The generate struct is also the suggest one from here, except I don't use a nested struct but a separate type. Using the suggested nested struct does not make a difference:
JSON-to-Go

Do I need to implement my own Unmarshaller for this?

======= UPDATE SOLUTION ============

It is enough to implement the UnmarshalJSON interface for the Point struct.
The example below does not contain proper error handling but it show the direction.

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)
}

答案1

得分: 12

JSON:

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

结构体:

type Vehicle struct {
  Type   string
  Wheels int
}

解码器:

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

英文:

The JSON:

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

The Struct:

type Vehicle struct {
  Type   string
  Wheels int
}

The 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

答案2

得分: 3

你需要为此实现自己的解码器。

你正在尝试将一个数组解码为一个结构体(Point),这意味着你需要告诉JSON解码器如何将数组的值映射到结构体的值。

此外,请注意你在Point定义中的标签是不正确的。JSON标签是指键名,但数组没有键(在JavaScript中,可以像有键一样访问,但这不是JavaScript)。换句话说,如果你的JSON看起来像{"0":123},那么json:"0"才能正常工作。如果你实现自己的解码器,你可以直接去掉这些json标签。

英文:

> Do I need to implement my own Unmarshaller for this?

Yes.

You're trying to unmarshal an array into a struct (Point), which means you need to tell the JSON unmarshaler how the array values map to the struct values.

Also note that your tags are incorrect in your Point definition. json tags refer to the key names, but arrays don't have keys (in JavaScript they can be accessed as if they do, but this isn't JavaScript). In other words, json:"0" will only work if your JSON looks like {"0":123}. If you implement your own unmarshaler, you can just get rid of those json tags.

huangapple
  • 本文由 发表于 2017年2月22日 05:06:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/42377989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定