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

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

How to Unmarshal list of varied types without defaulting to float64

问题

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

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

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

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

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

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

  1. [int64, map[string]interface{}]

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

以下是我的代码:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. func main() {
  7. j := `[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]`
  8. b := []byte(j)
  9. fmt.Println(j)
  10. var line []interface{}
  11. var ints []int64
  12. json.Unmarshal(b, &line)
  13. fmt.Println(line)
  14. // fmt.Println(line[0].(int64)) - this doesn't work
  15. fmt.Println(line[0].(float64))
  16. fmt.Println(int64(line[0].(float64)))
  17. json.Unmarshal(b, &ints)
  18. fmt.Println(ints)
  19. }

输出如下:

  1. [1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]
  2. [1.416495600501596e+18 map[venue_id:73 message_type:ABC sequence:2.6686695e+07]]
  3. 1.416495600501596e+18
  4. 1416495600501595904
  5. [1416495600501595942 0]

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

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "bytes"
  6. )
  7. func main() {
  8. j := `[1416495600501595942, {"venue_id": 7.3, "message_type": "ABC", "sequence": 26686695}]`
  9. b := []byte(j)
  10. fmt.Println(j)
  11. var line []interface{}
  12. d := json.NewDecoder(bytes.NewBuffer(b))
  13. d.UseNumber()
  14. if err := d.Decode(&line); err != nil {
  15. panic(err)
  16. }
  17. fmt.Println(line[0])
  18. data := line[1].(map[string]interface{})
  19. fmt.Println(data["venue_id"])
  20. fmt.Println(data["sequence"])
  21. }

希望对你有帮助!

英文:

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

  1. [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

  1. [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 ...)

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. func main() {
  7. j := `[1416495600501595942, {"venue_id": 73, "message_type": "ABC", "sequence": 26686695}]`
  8. b := []byte(j)
  9. fmt.Println(j)
  10. var line []interface{}
  11. var ints []int64
  12. json.Unmarshal(b, &line)
  13. fmt.Println(line)
  14. // fmt.Println(line[0].(int64)) - this doesn't work
  15. fmt.Println(line[0].(float64))
  16. fmt.Println(int64(line[0].(float64)))
  17. json.Unmarshal(b, &ints)
  18. fmt.Println(ints)
  19. }

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)

  1. package main
  2. import (
  3. &quot;encoding/json&quot;
  4. &quot;fmt&quot;
  5. &quot;bytes&quot;
  6. )
  7. func main() {
  8. j := `[1416495600501595942, {&quot;venue_id&quot;: 7.3, &quot;message_type&quot;: &quot;oKC&quot;, &quot;sequence&quot;: 26686695}]`
  9. b := []byte(j)
  10. fmt.Println(j)
  11. var line []interface{}
  12. d := json.NewDecoder(bytes.NewBuffer(b))
  13. d.UseNumber()
  14. if err := d.Decode(&amp;line); err != nil {
  15. panic(err)
  16. }
  17. fmt.Println(line[0])
  18. data := line[1].(map[string]interface{})
  19. fmt.Println(data[&quot;venue_id&quot;])
  20. fmt.Println(data[&quot;sequence&quot;])
  21. }

答案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:

确定