英文:
JSON unmarshaling with long numbers gives floating point number
问题
我正在使用golang进行JSON的编组和解组,但是当我想要处理数字字段时,golang会将其转换为浮点数而不是长整数,例如:
我有以下JSON:
{
"id": 12423434,
"Name": "Fernando"
}
将其编组为一个map,然后再解组为JSON字符串后,我得到:
{
"id":1.2423434e+07,
"Name":"Fernando"
}
如你所见,"id"字段以浮点数表示。
我正在使用以下代码:
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
//创建JSON字符串
var b = []byte(`
{
"id": 12423434,
"Name": "Fernando"
}
`)
//将JSON解组为map
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
//打印map
fmt.Println(m)
//将map编组为JSON
result, _ := json.Marshal(m)
//打印JSON
os.Stdout.Write(result)
}
它输出:
map[id:1.2423434e+07 Name:Fernando]
{"Name":"Fernando","id":1.2423434e+07}
似乎是第一次编组为map时生成了浮点数。我该如何将其修复为长整数?
这是在goland playground中的程序链接:
http://play.golang.org/p/RRJ6uU4Uw-
英文:
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 and unmarshal
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-
答案1
得分: 85
有时候,你可能无法提前定义一个结构体,但仍然需要将数字传递给编组-解组过程而不改变其值。
在这种情况下,你可以在json.Decoder
上使用UseNumber
方法,它会导致所有数字解组为json.Number
(即数字的原始字符串表示)。这对于在JSON中存储非常大的整数也很有用。
例如:
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)
}
结果:
decoded to map[string]interface {}{"id":"12423434", "Name":"Fernando"}
encoded to {"Name":"Fernando","id":12423434}
英文:
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}
答案2
得分: 17
JSON标准不支持长整型或浮点型,它只支持数字。当你没有定义其他类型时(即只提供了Unmarshal
函数的interface{}
参数),json
包会默认将其解析为float64类型。
你应该创建一个合适的结构体(正如Volker所提到的):
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
func main() {
//创建Json字符串
var b = []byte(`{"id": 12423434, "Name": "Fernando"}`)
//将Json解析为合适的结构体
var f Person
json.Unmarshal(b, &f)
//打印Person
fmt.Println(f)
//将结构体解析为Json
result, _ := json.Marshal(f)
//打印Json
os.Stdout.Write(result)
}
结果:
{12423434 Fernando}
{"id":12423434,"name":"Fernando"}
Playground链接:http://play.golang.org/p/2R76DYVgMK
编辑:
如果你有一个动态的Json结构,并且希望使用结构体的优势,你可以使用json.RawMessage
来解决。json.RawMessage
类型的变量将存储原始的JSON字符串,这样当你知道它包含的对象类型时,可以将其解析为相应的结构体。无论你使用哪种解决方案,你都需要使用if
或switch
语句来确定结构体的类型。
当JSON数据的某些部分只需要复制到另一个JSON对象中时,这也非常有用,比如JSON RPC请求的id
值。
使用json.RawMessage
的容器结构体示例及相应的JSON数据:
type Container struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
var b = []byte(`{"type": "person", "data":{"id": 12423434, "Name": "Fernando"}}`)
在Playground上修改后的示例:http://play.golang.org/p/85s130Sthu
编辑2:
如果你的JSON值的结构是基于名称/值对的名称,你可以使用以下方式:
type Container map[string]json.RawMessage
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论