如何在不默认为float64的情况下解组包含不同类型的列表

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

How to Unmarshal list of varied types without defaulting to float64

问题

我有以下的JSON数据(来自外部程序,稍作简化)我无法更改JSON格式。

[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]

我在Go语言中解析它时遇到了问题,我认为主要是因为它是一个不同类型的列表。显而易见的做法似乎是使用[]interface{},这样可以工作,但它会将其转换为float64,这会产生一个我无法处理的舍入误差(该数字是自纪元以来的纳秒时间戳)。

我可以通过两次解析来使其工作,即[]interface{}[]int64,但这显然会影响性能,而且我正在实时处理大量数据。

我尝试使用结构体,因为它会被视为映射而不是列表[]

是否有任何方法可以传递数据的格式,或者使其默认为int64而不是float64?它的格式总是这样的:

[int64, map[string]interface{}]

也就是说,我知道上层列表的格式,以及映射的键是字符串,但值可以是任何类型(包括小数,我认为我只能将它们解释为浮点数...)

以下是我的代码:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    j := `[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]`
    b := []byte(j)
    fmt.Println(j)
    var line []interface{}
    var ints []int64

    json.Unmarshal(b, &line)
    fmt.Println(line)
    // fmt.Println(line[0].(int64))  - this doesn't work
    fmt.Println(line[0].(float64))
    fmt.Println(int64(line[0].(float64)))

    json.Unmarshal(b, &ints)
    fmt.Println(ints)
}

输出如下:

[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]
[1.416495600501596e+18 map[venue_id:73 message_type:ABC sequence:2.6686695e+07]]
1.416495600501596e+18
1416495600501595904
[1416495600501595942 0]

解决方案(感谢makpoc / dystroy):

package main

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

func main() {
    j := `[1416495600501595942, {"venue_id": 7.3, "message_type": "ABC", "sequence": 26686695}]`
    b := []byte(j)
    fmt.Println(j)
    var line []interface{}
    
    d := json.NewDecoder(bytes.NewBuffer(b))
    d.UseNumber()
    if err := d.Decode(&line); err != nil {
        panic(err)
    }
    fmt.Println(line[0])
    data := line[1].(map[string]interface{})
    fmt.Println(data["venue_id"])
    fmt.Println(data["sequence"])
}

希望对你有帮助!

英文:

I have the following json data (from an external program, simplified a bit)
I can't change the json format.

[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]

I'm having trouble unpacking it in Go, I think mostly because it's a list of disparate types.
The obvious thing to do seems to be to do is []interface{}, which works, but in converts it to a float64, which produces a roundoff error that I can't deal with (the number is a timestamp since epoch in nanos).

I can get it to work by unpacking it twice, as []interface{} and []int64, but that's obviously going to hinder performance, and I'm processing large amounts of data in real time.
I tried using struct here because that would get treated as a map, not a list[]

Is there any way to either pass it the format of the data, or make it default to int64 instead of float64? It's always going to be

[int64, map[string]interface{}]

ie I know the format of the upper level list, and that the keys of the map are strings, but the values could be anything (painfully, including decimals, which I think the only thing I can use to interpret them as is floats ...)

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    j := `[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]`
    b := []byte(j)
    fmt.Println(j)
    var line []interface{}
    var ints []int64

    json.Unmarshal(b, &line)
    fmt.Println(line)
    // fmt.Println(line[0].(int64))  - this doesn't work
    fmt.Println(line[0].(float64))
    fmt.Println(int64(line[0].(float64)))

    json.Unmarshal(b, &ints)
    fmt.Println(ints)
}

Output is as follows:

<pre>
[1416495600501595942, {"venue_id": 73, "message_type": "oKC", "sequence": 26686695}]
[1.416495600501596e+18 map[venue_id:73 message_type:oKC sequence:2.6686695e+07]]
1.416495600501596e+18
1416495600501595904
[1416495600501595942 0]
</pre>

Solution (thanks to makpoc / dystroy)

package main

import (
    &quot;encoding/json&quot;
    &quot;fmt&quot;
    &quot;bytes&quot;
)

func main() {
    j := `[1416495600501595942, {&quot;venue_id&quot;: 7.3, &quot;message_type&quot;: &quot;oKC&quot;, &quot;sequence&quot;: 26686695}]`
    b := []byte(j)
    fmt.Println(j)
    var line []interface{}
    
    d := json.NewDecoder(bytes.NewBuffer(b))
    d.UseNumber()
        if err := d.Decode(&amp;line); err != nil {
        panic(err)
    }
    fmt.Println(line[0])
    data := line[1].(map[string]interface{})
    fmt.Println(data[&quot;venue_id&quot;])
    fmt.Println(data[&quot;sequence&quot;])
}

答案1

得分: 1

根据这个答案,你可以使用Decoder -> 和UseNumber或者一个结构体来代替直接解析值。

英文:

According to this answer you can use Decoder -> and UseNumber or a struct instead of directly parsing the value.

huangapple
  • 本文由 发表于 2014年11月25日 22:27:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/27129263.html
匿名

发表评论

匿名网友

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

确定