0

In my Go code, I was working to unmarshal a JSON payload we receive from an endpoint. This endpoint encodes large values in scientific notation:

type Test struct {
    Key   string
    Value int32
}

func main() {
    data := []byte("{\"Key\": \"derp\", \"Value\": 3.898733e+06}")

    var t *Test
    err := json.Unmarshal(data, &t)
    
    fmt.Printf("Error: %v\n", err)
    fmt.Printf("Data: %v\n", t)
}

The encoded value here is equivalent to 3,898,733 in standard notation. However, this code prints an error:

json: cannot unmarshal number 3.898733e+06 into Go struct field Test.Value of type int32

This makes sense because strconv also fails to parse this value from a string. However, I can do var i int32 = 3.898733e+06 and that compiles and produces the correct answer.

So, how can I address this error?

Woody1193
  • 7,252
  • 5
  • 40
  • 90
  • 1
    That's unfortunate because the value is appropriate for the type (the value is an integer that fits in an `int32`). A workaround is to use a float type or [json.Number](https://pkg.go.dev/encoding/json#Number) for the field. –  Jun 09 '22 at 03:00
  • @RedBlue That was my unfortunate conclusion as well. This also introduces some legitimate problems because the documentation for this endpoint doesn't specify the type of data returned beyond stating that it's numeric and so I was hoping to determine some constraints on the data itself. – Woody1193 Jun 09 '22 at 03:05
  • I just want to say that having this question marked as a duplicate is personally annoying to me. I searched SO for this problem and wasn't able to find anything. It feels like I'm being penalized for not knowing the exact wording someone else used when asking. A bit meta perhaps, but there it is. – Woody1193 Jun 09 '22 at 07:12

1 Answers1

1

The declaration var i int32 = 3.898733e+06 works because the literal in this expression is an untyped constant, and untyped constants are evaluated based on context. In this case, even though it is written as a floating-point number, it is interpreted as an int32 at compile time. This does not happen at runtime.

There are several options to make this work for JSON marshaling:

  1. Declare the Value as json.Number. This way you can try parsing it as int64, and if that fails, parse it as float64, and convert to int64 and hope you don't lose precision.
  2. Define a custom type and unmarshal yourself:
type LongInt int32

func (i *LongInt) UnmarshalJSON(data []byte) error {
   // Parse data yourself and set the int value
   // Maybe use big.Int?
}
  1. Declare Value as float64
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59