如何在JSON中编组和解组big.Int?

The question How to marshal JSON with bigints? is about marshaling big.Int values into strings in JSON. This question asks, how does one marshal and unmarshal big.Int values natively as numbers in JSON?

Passing around large values marshaled in this manner may be incompatible with other implementations of JSON, particularly JavaScript and jq, as RFC 7159 notes:

Note that when such software is used, numbers that are integers and are in the range [-(2**53)+1, (2**53)-1] are interoperable in the sense that implementations will agree exactly on their numeric values.

Create a custom type BigInt which implements json.Marshaler and json.Unmarshaler like the following:

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
}

Rationale:

  • Implemented as a struct type embedding big.Int instead of as a subtype of big.Int so that the methods (Add, SetString, etc.) are kept
  • MarshalJSON takes a value receiver so that marshaled values using BigInt don't have to use pointers
  • UnmarshalJSON takes a pointer receiver because it modifies the receiver; however, types using BigInt still don't have to use pointers
  • UnmarshalJSON works on a temporary value because the SetString method has undefined behavior on its receiver in the case of errors

As with big.Int the zero value is useful and is equal to the number 0.