Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decode JSON value which can be either string or number

Tags:

json

go

When I make an HTTP call to a REST API I may get the JSON value count back as a Number or String. I'ld like to marshal it to be an integer in either case. How can I deal with this in Go?.

like image 935
Jason Leach Avatar asked May 10 '26 08:05

Jason Leach


2 Answers

Use the "string" field tag option to specify that strings should be converted to numbers. The documentation for the option is:

The "string" option signals that a field is stored as JSON inside a JSON-encoded string. It applies only to fields of string, floating point, integer, or boolean types. This extra level of encoding is sometimes used when communicating with JavaScript programs:

Here's an example use:

type S struct {
    Count int `json:"count,string"`
}

playground example

If the JSON value can be number or string, then unmarshal to interface{} and convert to int after unmarshaling:

Count interface{} `json:"count,string"`

Use this function to convert the interface{} value to an int:

func getInt(v interface{}) (int, error) {
  switch v := v.(type) {
  case float64:
    return int(v), nil
  case string:
    c, err := strconv.Atoi(v)
    if err != nil {
       return 0, err
    }
    return c, nil
  default:
    return 0, fmt.Errorf("conversion to int from %T not supported", v)
  }
}

go standard lib "json/encoding" offers a json.Number type for parsing both string number and integers in JSON data.

Here's an example use:

type Foo struct {
    Value json.Number `json:"value"`
}

json.Number is actually an alias for string, json.Unmarshal handles it specially during unmarshalling to support both formats, like "123" and 123.

Once you have a json.Number, you can extract its numerical representation using the methods .Int64() or .Float64() according to the need.

playground example:

package main

import (
    "encoding/json"
    "fmt"
)

type Foo struct {
    Value json.Number `json:"value"`
}

func main() {
    // json int -> json.Number
    var foo Foo
    if err := json.Unmarshal([]byte(`{"value": 123}`), &foo); err != nil {
        fmt.Println("json unmarshal err:", err)
        return
    }
    fmt.Printf("foo: %+v\n", foo)

    // json string -> json.Number
    var foo2 Foo
    if err := json.Unmarshal([]byte(`{"value": "123"}`), &foo2); err != nil {
        fmt.Println("json unmarshal err:", err)
        return
    }
    fmt.Printf("foo2: %+v\n", foo2)

    // json.Number -> int64
    intValue, err := foo.Value.Int64()
    if err != nil {
        fmt.Println("value.Int64() err:", err)
        return
    }

    fmt.Println("intValue:", intValue)
}

output:

foo: {Value:123}
foo2: {Value:123}
intValue: 123
like image 33
valleygtc Avatar answered May 12 '26 02:05

valleygtc