JSON解组时,长数字会被解析为浮点数。

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

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字符串,这样当你知道它包含的对象类型时,可以将其解析为相应的结构体。无论你使用哪种解决方案,你都需要使用ifswitch语句来确定结构体的类型。

当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

huangapple
  • 本文由 发表于 2014年3月12日 13:53:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/22343083.html
匿名

发表评论

匿名网友

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

确定