停止 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.Numberfloat64值,只需调用它的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