JSON unmarshaling with long numbers gives floating point number
Solution 1
There are times when you cannot define a struct in advance but still require numbers to pass through the marshal-unmarshal process unchanged.
In that case you can use the UseNumber
method on json.Decoder
, which causes all numbers to unmarshal as json.Number
(which is just the original string representation of the number). This can also useful for storing very big integers in JSON.
For example:
package main
import (
"strings"
"encoding/json"
"fmt"
"log"
)
var data = `{
"id": 12423434,
"Name": "Fernando"
}`
func main() {
d := json.NewDecoder(strings.NewReader(data))
d.UseNumber()
var x interface{}
if err := d.Decode(&x); err != nil {
log.Fatal(err)
}
fmt.Printf("decoded to %#v\n", x)
result, err := json.Marshal(x)
if err != nil {
log.Fatal(err)
}
fmt.Printf("encoded to %s\n", result)
}
Result:
decoded to map[string]interface {}{"id":"12423434", "Name":"Fernando"}
encoded to {"Name":"Fernando","id":12423434}
Solution 2
The JSON standard doesn't have longs or floats, it only has numbers. The json
package will assume float64 when you haven't defined anything else (meaning, only provided Unmarshal
with an interface{}
).
What you should do is to create a proper struct (as Volker mentioned):
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
func main() {
//Create the Json string
var b = []byte(`{"id": 12423434, "Name": "Fernando"}`)
//Marshal the json to a proper struct
var f Person
json.Unmarshal(b, &f)
//print the person
fmt.Println(f)
//unmarshal the struct to json
result, _ := json.Marshal(f)
//print the json
os.Stdout.Write(result)
}
Result:
{12423434 Fernando}
{"id":12423434,"name":"Fernando"}
Playground: http://play.golang.org/p/2R76DYVgMK
Edit:
In case you have a dynamic json structure and wish to use the benefits of a struct, you can solve it using json.RawMessage
. A variable of type json.RawMessage
will store the raw JSON string so that you later on, when you know what kind of object it contains, can unmarshal it into the proper struct. No matter what solution you use, you will in any case need some if
or switch
statement where you determine what type of structure it is.
It is also useful when parts of the JSON data will only be copied to the another JSON object such as with the id
-value of a JSON RPC request.
Example of container struct using json.RawMessage and the corresponding JSON data:
type Container struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
var b = []byte(`{"type": "person", "data":{"id": 12423434, "Name": "Fernando"}}`)
A modified version of your example on Playground: http://play.golang.org/p/85s130Sthu
Edit2:
If the structure of your JSON value is based on the name of a name/value pair, you can do the same with a:
type Container map[string]json.RawMessage
Fersca
Software Engineer, like java, groovy/grails, golang. Product Development Manager at Mercadolibre.com
Updated on July 09, 2022Comments
-
Fersca almost 2 years
I was marshaling and unmarshaling JSONs using golang and when I want to do it with number fields golang transforms it in floating point numbers instead of use long numbers, for example.
I have the following JSON:
{ "id": 12423434, "Name": "Fernando" }
After
marshal
it to a map andunmarshal
again to a json string I get:{ "id":1.2423434e+07, "Name":"Fernando" }
As you can see the
"id"
field is in floating point notation.The code that I am using is the following:
package main import ( "encoding/json" "fmt" "os" ) func main() { //Create the Json string var b = []byte(` { "id": 12423434, "Name": "Fernando" } `) //Marshal the json to a map var f interface{} json.Unmarshal(b, &f) m := f.(map[string]interface{}) //print the map fmt.Println(m) //unmarshal the map to json result,_:= json.Marshal(m) //print the json os.Stdout.Write(result) }
It prints:
map[id:1.2423434e+07 Name:Fernando] {"Name":"Fernando","id":1.2423434e+07}
It appears to be that the first
marshal
to the map generates the FP. How can I fix it to a long?This is the link to the program in the goland playground: http://play.golang.org/p/RRJ6uU4Uw-
-
peterSO about 10 yearsThe comment "//Marshal the json to a map" should have been changed to "//Marshal the json to a struct".
-
ANisus about 10 years@peterSo: I just noticed I missed the comments. Fixed it "struct"
-
Fersca about 10 yearsThe problem y I am storing different json structures, so I do not know which would be de struct before reading the json
-
Fersca about 10 yearsNice! That was what I was looking for, I'll try to use it and give feedback.
-
ANisus about 10 years@Fersca: Then you most likely have some field that would tell you what structure you are about to read. See my edit on how to handle such dynamic data structures.
-
linqu over 9 yearsGreat, I had concerns that real floats get also casted to integers. Fortunately it does not: play.golang.org/p/TRo0v4yeBO
-
Claire Sannier almost 9 years@Fersca This seems like the answer; if you're still around, accept it maybe?
-
Ado Ren almost 4 yearsIs there usecases where it's bad to UseNumber ? I can't see the drawbacks.
-
rog almost 4 yearsIf you use UseNumber, you don't get numeric values in your unmarshaled JSON - it's just a string. Every time you want to use one, the string is parsed again (which isn't very efficient) and you need to check the error on the method return, which is more awkward than just using the value directly.