停止 json.Marshal() 从浮点数中去除尾随零
Stop json.Marshal() from stripping trailing zero from floating point number
我遇到了以下问题:
我的 golang 程序将一些信息转换成 JSON。
例如,结果如下 json:
{
"value":40,
"unit":"some_string"
}
问题是 "input" 的值为 40.0,编组会去除尾随零。如果读取 JSON 的 EPL 能够在没有 .0
的情况下将 40 读取为浮点数,那将是没有问题的
所以 JSON 输出应该如下所示:
{
"value":40.0,
"unit":"some_string"
}
是否有可能 "stop" json.Marshal() 删除零?
编辑:值必须是浮点数
将值保存为字符串并在需要时将其转换回。
默认情况下,如果浮点数的值为整数值,则呈现时不带小数点和分数。表示形式较短,表示相同的数字。
如果您想控制数字在 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>
另一个方向,如果你想要一个json.Number
的float64
值,只需调用它的Number.Float64()
方法。
@icza 提供了一个很好的答案,但只是为了提供另一种选择,您可以定义自己的 float 类型并为其定义自己的序列化。像这样
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>
。 Check it out in playground.
我有一个类似的问题,我想将 map[string]interface{}
编组为浮点值 f.x 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
这样的浮点数
type MyFloat float64
func (mf MyFloat) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%.1f", float64(mf))), nil
}
像这样使用
type PricePoint struct {
Price MyFloat `json:"price"`
From time.Time `json:"valid_from"`
To time.Time `json:"valid_to"`
}
用您需要的精度替换 "%.1f"
中的 1
我遇到了以下问题: 我的 golang 程序将一些信息转换成 JSON。 例如,结果如下 json:
{
"value":40,
"unit":"some_string"
}
问题是 "input" 的值为 40.0,编组会去除尾随零。如果读取 JSON 的 EPL 能够在没有 .0
的情况下将 40 读取为浮点数,那将是没有问题的所以 JSON 输出应该如下所示:
{
"value":40.0,
"unit":"some_string"
}
是否有可能 "stop" json.Marshal() 删除零?
编辑:值必须是浮点数
将值保存为字符串并在需要时将其转换回。
默认情况下,如果浮点数的值为整数值,则呈现时不带小数点和分数。表示形式较短,表示相同的数字。
如果您想控制数字在 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>
另一个方向,如果你想要一个json.Number
的float64
值,只需调用它的Number.Float64()
方法。
@icza 提供了一个很好的答案,但只是为了提供另一种选择,您可以定义自己的 float 类型并为其定义自己的序列化。像这样
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>
。 Check it out in playground.
我有一个类似的问题,我想将 map[string]interface{}
编组为浮点值 f.x 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
type MyFloat float64
func (mf MyFloat) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%.1f", float64(mf))), nil
}
像这样使用
type PricePoint struct {
Price MyFloat `json:"price"`
From time.Time `json:"valid_from"`
To time.Time `json:"valid_to"`
}
用您需要的精度替换 "%.1f"
中的 1