【问题标题】:How to marshal and unmarshal big.Int in JSON?如何在 JSON 中编组和解组 big.Int?
【发布时间】:2018-12-31 22:21:48
【问题描述】:

How to marshal JSON with bigints? 的问题是关于将 big.Int 值编组为 JSON 中的字符串。这个问题问,一个编组 和解组 big.Int 如何在 JSON 中将原生值 作为数字

以这种方式传递大值可能与 JSON 的其他实现不兼容,尤其是 JavaScript 和 jq,正如 RFC 7159 所指出的:

请注意,当使用此类软件时,整数和数字 在[-(2**53)+1, (2**53)-1] 范围内可互操作 感觉实现将完全同意他们的数字 价值观。

【问题讨论】:

  • @Flimzy JSON spec 没有为数字指定任何最大值(或最小值)。由于将所有数字都视为双精度浮点值,JavaScript 在处理大于 2^54 的整数时可能会丢失精度,但并非所有语言都如此,特别是 go 并非如此。事实上,使用int64 与 JavaScript 兼容性具有相同的含义,但在 go 中仍然很常用。
  • RFC 7159 警告不要在 JSON 中使用 [-(2^53)+1, (2^53)-1] 范围之外的数字,因此您所尝试的通常不被认为是可移植的,并且通常应该避免。
  • 我在问题中添加了关于 RFC 的注释。我仍然认为这是对 JSON 的有效使用,尽管它的前两个字母是通用的序列化格式。使用字符串有其自身的问题,包括双方都同意它们是数字作为字符串,以及 JavaScript 客户端不将它们强制为数字。
  • 我还添加了关于jq处理大整数的相关讨论的链接;通过jq 传递这些值之一不能保证是无损转换。

标签: json go biginteger


【解决方案1】:

创建一个自定义类型BigInt,它实现json.Marshalerjson.Unmarshaler,如下所示:

import (
    "fmt"
    "math/big"
)

type BigInt struct {
    big.Int
}

func (b BigInt) MarshalJSON() ([]byte, error) {
    return []byte(b.String()), nil
}

func (b *BigInt) UnmarshalJSON(p []byte) error {
    if string(p) == "null" {
        return nil
    }
    var z big.Int
    _, ok := z.SetString(string(p), 10)
    if !ok {
        return fmt.Errorf("not a valid big integer: %s", p)
    }
    b.Int = z
    return nil
}

理由:

  • 实现为嵌入big.Int 的结构类型,而不是big.Int 的子类型,以便保留方法(Add、SetString 等)
  • MarshalJSON 采用值接收器,因此使用 BigInt 的封送值不必使用指针
  • UnmarshalJSON 采用指针接收器,因为它修改了接收器;但是,使用 BigInt 的类型仍然不必使用指针
  • UnmarshalJSON 适用于临时值,因为在发生错误时 SetString 方法在其接收器上具有未定义的行为

big.Int 一样,零值很有用,它等于数字 0。

【讨论】:

  • 感谢您的提示!请注意,此解决方案对数字类型进行编码/解码,因为输出未包含在引号中,因此可能会丢失精度。建议用引号括起来或用字符串值包装 json.(Un)Marshal。
  • 是的,只有当您使用相同的代码读写 JSON 时,此解决方案才真正适合。如果你传递 JSON 并跨越语言或工具边界,你必然会失去精度(例如 jq 将所有数字转换为双精度浮点数)。我认为一般来说字符串是更好的方法。
猜你喜欢
  • 2018-12-15
  • 1970-01-01
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
  • 2014-04-21
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多