Подтвердить что ты не робот

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-

4b9b3361

Ответ 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