JSON unmarshaling с длинными числами дает число с плавающей запятой
Я выполнял маршалинг и демаршализацию JSON с использованием golang, и когда я хочу сделать это с числовыми полями, golang, например, преобразует его в числа с плавающей запятой вместо использования длинных чисел.
У меня есть следующий JSON:
{
"id": 12423434,
"Name": "Fernando"
}
После того, как marshal
его на карту и unmarshal
снова в строку JSON я получаю:
{
"id":1.2423434e+07,
"Name":"Fernando"
}
Как видите, поле "id"
находится в нотации с плавающей запятой.
Код, который я использую, следующий:
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)
}
Это печатает:
map[id:1.2423434e+07 Name:Fernando]
{"Name":"Fernando","id":1.2423434e+07}
Похоже, что первый marshal
на карте генерирует FP. Как я могу исправить это долго?
Это ссылка на программу на детской площадке в Голанде: http://play.golang.org/p/RRJ6uU4Uw-
Ответы
Ответ 1
Есть моменты, когда вы не можете определить структуру заранее, но по-прежнему требуется, чтобы числа проходили через процесс маршала-маршала без изменений.
В этом случае вы можете использовать UseNumber
метод на json.Decoder
, что вызывает все номера, чтобы распаковать, как 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}
Ответ 2
стандарт JSON не имеет длинных или плавающих чисел, он имеет только цифры. Пакет json
будет принимать float64, если вы еще не определили что-либо еще (что означает Unmarshal
только interface{}
).
Что вы должны сделать, так это создать правильную структуру (как упоминал Фолькер):
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)
}
Результат:
{12423434 Фернандо}
{ "Идентификатор": 12423434, "название": "Фернандо" }
Игровая площадка: http://play.golang.org/p/2R76DYVgMK
Edit:
Если у вас есть динамическая структура json и вы хотите использовать преимущества структуры, вы можете решить ее с помощью json.RawMessage
. Переменная типа json.RawMessage
будет хранить необработанную строку JSON, чтобы позже, когда вы знаете, какой именно объект она содержит, можно развязать ее в правильную структуру. Независимо от того, какое решение вы используете, в любом случае вам понадобится инструкция if
или switch
, в которой вы определяете, какой тип структуры она имеет.
Это также полезно, когда части данных JSON будут скопированы только на другой объект JSON, например, с id
-значением запроса JCON RPC.
Пример структуры контейнера с использованием 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"}}`)
Измененная версия вашего примера на игровой площадке: http://play.golang.org/p/85s130Sthu
Edit2:
Если структура вашего значения JSON основана на имени пары имя/значение, вы можете сделать то же самое с помощью:
type Container map[string]json.RawMessage