Dwarves
Memo
Type ESC to close search bar

Go JSON parser: number <-> interface

Go JSON parser: number <-> interface

TLDR; becareful when using map[string]interface{} to hold json number value, use custom decoder with newDecoder.UseNumber() to decode the json string.

The problem

type payload struct { ID int64 `json:"id"` }
p := payload{ ID: 98470950831393239 }

raw, _ := json.Marshal(p)
fmt.Printf("version1 is %s\n", raw) // {"id":98470950831393239}

var obj map[string]interface{}
json.Unmarshal(raw, &obj) // id will be parsed as float64
interfaceRaw, _ := json.Marshal(obj)
fmt.Printf("version2 is %s\n", interfaceRaw) // {"id":98470950831393230 }

Why The issue caused by default Go uses float64 for interface{} parsing Ref: https://cs.opensource.google/go/go/+/refs/tags/go1.19.3:src/encoding/json/decode.go;l=844;drc=a11cd6f69aec5c783656601fbc7b493e0d63f605

Solution To resolve we need custom decoder with UseNumber

var obj map[string]interface{}
decoder := json.NewDecoder(strings.NewReader(string(raw)))
decoder.UseNumber()
decoder.Decode(&obj)
interfaceRaw, _ := json.Marshal(obj)
fmt.Printf("version2 is %s\n", interfaceRaw) // {"id":98470950831393239 }