在Go语言中解析JSON时保留int64值。

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

preserve int64 values when parsing json in Go

问题

我正在处理一个包含64位整数对象数组的Go中的json POST。当使用json.Unmarshal时,这些值似乎被转换为float64,这并不是很有帮助。

body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
    panic(err)
}

tags := dat["tags"].([]interface{})

for i, tag := range tags {
    fmt.Println("tag: ", i, " id: ", tag.(map[string]interface{})["id"].(int64))
}

有没有办法在json.Unmarshal的输出中保留原始的int64?

以上代码的Go Playground

英文:

I'm processing a json POST in Go that contains an array of objects containing 64bit integers. When using json.Unmarshal these values seem to be converted to a float64 which isn't very helpful.

body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
	panic(err)
}

tags := dat["tags"].([]interface{})

for i, tag := range tags {

	fmt.Println("tag: ", i, " id: ", tag.(map[string]interface{})["id"].(int64))

}

Is there any way to preserve the original int64 in the output of json.Unmarshal?

Go Playground of above code

答案1

得分: 39

解决方案1

您可以使用DecoderUseNumber来解码您的数字而不会丢失:

Number类型的定义如下:

// Number表示一个JSON数字字面量。
type Number string

这意味着您可以轻松地进行转换:

package main

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

func main() {
    body := []byte("{\"tags\":[{\"id\":4418489049307132905},{\"id\":4418489049307132906}]}")
    dat := make(map[string]interface{})
    d := json.NewDecoder(bytes.NewBuffer(body))
    d.UseNumber()
    if err := d.Decode(&dat); err != nil {
        panic(err)
    }
    tags := dat["tags"].([]interface{})
    n := tags[0].(map[string]interface{})["id"].(json.Number)
    i64, _ := strconv.ParseUint(string(n), 10, 64)
    fmt.Println(i64) // 输出 4418489049307132905
}

解决方案2

您还可以解码为特定的结构以满足您的需求:

package main

import (
    "encoding/json"
    "fmt"
)

type A struct {
    Tags []map[string]uint64 // "tags"
}

func main() {
    body := []byte("{\"tags\":[{\"id\":4418489049307132905},{\"id\":4418489049307132906}]}")
    var a A
    if err := json.Unmarshal(body, &a); err != nil {
        panic(err)
    }
    fmt.Println(a.Tags[0]["id"]) // 输出 4418489049307132905
}

个人而言,我通常更喜欢这个解决方案,因为它更有结构化和易于维护的感觉。

注意

如果您使用JSON,因为您的应用程序部分使用JavaScript:JavaScript没有64位整数,只有一种数字类型,即IEEE754双精度浮点数。因此,您将无法在JavaScript中使用标准解析函数解析此JSON而不会丢失数据。

英文:

Solution 1

You can use a Decoder and UseNumber to decode your numbers without loss :

The Number type is defined like this :

// A Number represents a JSON number literal.
type Number string

which means you can easily convert it :

package main

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

func main() {
	body := []byte("{\"tags\":[{\"id\":4418489049307132905},{\"id\":4418489049307132906}]}")
	dat := make(map[string]interface{})
	d := json.NewDecoder(bytes.NewBuffer(body))
	d.UseNumber()
	if err := d.Decode(&dat); err != nil {
		panic(err)
	}
	tags := dat["tags"].([]interface{})
	n := tags[0].(map[string]interface{})["id"].(json.Number)
	i64, _ := strconv.ParseUint(string(n), 10, 64)
	fmt.Println(i64) // prints 4418489049307132905
}

Solution 2

You can also decode into a specific structure tailored to your needs :

package main

import (
	"encoding/json"
	"fmt"
)

type A struct {
	Tags []map[string]uint64 // "tags"
}

func main() {
	body := []byte("{\"tags\":[{\"id\":4418489049307132905},{\"id\":4418489049307132906}]}")
	var a A
	if err := json.Unmarshal(body, &a); err != nil {
		panic(err)
	}
	fmt.Println(a.Tags[0]["id"]) // logs 4418489049307132905
}

Personally I generally prefer this solution which feels more structured and easier to maintain.

Caution

A small note if you use JSON because your application is partly in JavaScript : JavaScript has no 64 bits integers but only one number type, which is the IEEE754 double precision float. So you wouldn't be able to parse this JSON in JavaScript without loss using the standard parsing function.

答案2

得分: 0

body := []byte({"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]})

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
panic(err)
}

tags := dat["tags"].([]interface{})

for i, tag := range tags {
fmt.Printf("tag: %v, id: %.0f", i, tag.(map[string]interface{})["id"].(float64))
}

英文:

easier one:

body := []byte(`{"tags":[{"id":4418489049307132905},{"id":4418489049307132906}]}`)

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
    panic(err)
}

tags := dat["tags"].([]interface{})

for i, tag := range tags {
    fmt.Printf("tag: %v, id: %.0f", i, tag.(map[string]interface{})["id"].(float64))
}

答案3

得分: 0

我意识到这是非常旧的,但这是我最终使用的解决方案

/* 
   跳过之前的代码,这只是将浮点数转换为整数,如果值与小数点后的内容相同
*/

f := tag.(map[string]interface{})["id"].(float64)
if math.Floor(f) == f {
  fmt.Println("整数标签: ", i, " id: ", int64(f))	
} else {
  fmt.Println("标签: ", i, " id: ", f)
}
英文:

I realize this is very old, but this is the solution I ended up using

/* 
   skipping previous code, this is just converting the float 
   to an int, if the value is the same with or without what's 
   after the decimal points
*/

f := tag.(map[string]interface{})["id"].(float64)
if math.Floor(f) == f {
  fmt.Println("int tag: ", i, " id: ", int64(f))	
} else {
  fmt.Println("tag: ", i, " id: ", f)
}

huangapple
  • 本文由 发表于 2013年6月6日 01:33:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/16946306.html
匿名

发表评论

匿名网友

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

确定