preserve int64 values when parsing json in Go

19,239

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.

Share:
19,239

Related videos on Youtube

sicr
Author by

sicr

Updated on January 20, 2021

Comments

  • sicr
    sicr over 3 years

    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

  • Paolo Bonzini
    Paolo Bonzini almost 4 years
    The output is wrong with this solution, since both ids are changed to 4418489049307132928.