10

I'm trying to create a JSON representation within Go using a map[string]interface{} type. I'm dealing with JSON strings and I'm having a hard time figuring out how to avoid the JSON unmarshaler to automatically deal with numbers as float64s. As a result the following error occurs.

Ex. "{ 'a' : 9223372036854775807}" should be map[string]interface{} = [a 9223372036854775807 but in reality it is map[string]interface{} = [a 9.2233720368547758088E18]

I searched how structs can be used to avoid this by using json.Number but I'd really prefer using the map type designated above.

2
  • 3
    JSON defaults to using float64 for all numbers: golang.org/pkg/encoding/json/#Unmarshal. If you want it to be an integer, you need to unmarshal it to a destination with an integer value (example, a map[string]int or a struct{ a int }). That, or accept that it'll be a float and simply type-convert it to an int. Commented Nov 20, 2017 at 20:24
  • I'm aware that Go's encoding/json defaults to use float64s for json numbers. Type conversion would be the easiest solution if it worked with large int64 values. I guess I would have to write a custom marshaler or specify the map type. Thanks Commented Nov 21, 2017 at 21:17

3 Answers 3

6

The go json.Unmarshal(...) function automatically uses float64 for JSON numbers. If you want to unmarshal numbers into a different type then you'll have to use a custom type with a custom unmarshaler. There is no way to force the unmarshaler to deserialize custom values into a generic map.

For example, here's how you could parse values of the "a" property as a big.Int.

package main

import (
  "encoding/json"
  "fmt"
  "math/big"
)

type MyDoc struct {
  A BigA `json:"a"`
}

type BigA struct{ *big.Int }

func (a BigA) UnmarshalJSON(bs []byte) error {
  _, ok := a.SetString(string(bs), 10)
  if !ok {
    return fmt.Errorf("invalid integer %s", bs)
  }
  return nil
}

func main() {
  jsonstr := `{"a":9223372036854775807}`
  mydoc := MyDoc{A: BigA{new(big.Int)}}

  err := json.Unmarshal([]byte(jsonstr), &mydoc)
  if err != nil {
    panic(err)
  }

  fmt.Printf("OK: mydoc=%#v\n", mydoc)
  // OK: mydoc=main.MyDoc{A:9223372036854775807}
}
Sign up to request clarification or add additional context in comments.

Comments

2
func jsonToMap(jsonStr string) map[string]interface{} {
    result := make(map[string]interface{})
    json.Unmarshal([]byte(jsonStr), &result)
    return result
}

Comments

0

You can still use map[string]interface{} but you need to change your json decode way. Here is an example with json.NewDecoder().

jsonData := `{ "a" : 9223372036854775807}`
jsonDecoder := json.NewDecoder(bytes.NewReader([]byte(jsonData)))
jsonDecoder.UseNumber() // enable useNumber for preventing float

var decodedData map[string]interface{}
err := jsonDecoder.Decode(&decodedData)
if err != nil {
    panic(err)
}

fmt.Println(decodedData) // prints map[a:9223372036854775807]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.