Остановить json.Marshal () для удаления конечного нуля из числа с плавающей запятой - PullRequest
0 голосов
/ 21 сентября 2018

У меня возникла следующая проблема: Моя программа golang преобразует некоторую информацию в JSON.Например, это приводит к следующему json:

{
   "value":40,
   "unit":"some_string"
}

Проблема в том, что «вход» для значения равен 40.0, и сортировка удаляет конечный ноль.Не будет проблем, если EPL, который читает JSON, сможет читать 40 как float без .0

Так что вывод JSON должен выглядеть следующим образом:

{
   "value":40.0,
   "unit":"some_string"
}

Есть ливозможность "остановить" json.Marshal () от удаления нуля?

Редактировать: Значение должно быть Float

Ответы [ 4 ]

0 голосов
/ 25 октября 2018

У меня была похожая проблема, когда я хотел маршалировать map[string]interface{} со значениями с плавающей запятой от fx 1.0 до JSON как 1.0.Я решил эту проблему, добавив пользовательскую функцию Marshal для пользовательского типа с плавающей точкой, а затем заменил поплавки на карте на пользовательский тип:

type customFloat float64

func (f customFloat) MarshalJSON() ([]byte, error) {
    if float64(f) == math.Trunc(float64(f)) {
        return []byte(fmt.Sprintf("%.1f", f)), nil
    }
    return json.Marshal(float64(f))
}

func replaceFloat(value map[string]interface{}) {
    for k, v := range value {
        switch val := v.(type) {
        case map[string]interface{}:
            replaceFloat(val)
        case float64:
            value[k] = customFloat(val)
        }
    }
}

Затем заменил все узлы float64:

replaceFloat(myValue)
bytes, err := json.Marshal(myValue)

Это напечатает поплавки как 1.0

0 голосов
/ 21 сентября 2018

По умолчанию числа с плавающей запятой отображаются без десятичной точки и дробей, если их значение является целым числом.Представление короче и означает одно и то же число.

Если вы хотите контролировать отображение числа в представлении JSON, используйте тип json.Number.

Пример:

type Pt struct {
    Value json.Number
    Unit  string
}

func main() {
    data, err := json.Marshal(Pt{json.Number("40.0"), "some_string"})
    fmt.Println(string(data), err)
}

Вывод (попробуйте на Go Playground ):

{"Value":40.0,"Unit":"some_string"} <nil>

Если у вас есть число в качестве значения float64, выможет преобразовать его в json.Number следующим образом:

func toNumber(f float64) json.Number {
    var s string
    if f == float64(int64(f)) {
        s = fmt.Sprintf("%.1f", f) // 1 decimal if integer
    } else {
        s = fmt.Sprint(f)
    }
    return json.Number(s)
}

Тестирование:

f := 40.0
data, err := json.Marshal(Pt{toNumber(f), "some_string"})
fmt.Println(string(data), err)

f = 40.123
data, err = json.Marshal(Pt{toNumber(f), "some_string"})
fmt.Println(string(data), err)

Вывод (попробуйте на Go Playground ):

{"Value":40.0,"Unit":"some_string"} <nil>
{"Value":40.123,"Unit":"some_string"} <nil>

В другом направлении, если вам нужно float64 значение json.Number, просто вызовите его метод Number.Float64().

0 голосов
/ 21 сентября 2018

@ icza дал хороший ответ, но просто для того, чтобы предложить другой вариант, вы можете определить свой собственный тип с плавающей точкой и свою собственную сериализацию для него.Вот так

type KeepZero float64

func (f KeepZero) MarshalJSON() ([]byte, error) {
    if float64(f) == float64(int(f)) {
        return []byte(strconv.FormatFloat(float64(f), 'f', 1, 32)), nil
    }
    return []byte(strconv.FormatFloat(float64(f), 'f', -1, 32)), nil
}

type Pt struct {
    Value KeepZero
    Unit  string
}

func main() {
    data, err := json.Marshal(Pt{40.0, "some_string"})
    fmt.Println(string(data), err)
}

В результате получается {"Value":40.0,"Unit":"some_string"} <nil>. Проверьте это на детской площадке.

0 голосов
/ 21 сентября 2018

Сохраните значение в виде строки и приведите его обратно, если оно вам нужно.

...