8

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?.

kostix
  • 51,517
  • 14
  • 93
  • 176
Jason Leach
  • 3,889
  • 7
  • 37
  • 54

3 Answers3

18

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)
  }
}
bogdanciobanu
  • 1,603
  • 1
  • 12
  • 14
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
0
// Format of your expected request
type request struct {
    ACTIVE    string `json:"active"`
    CATEGORY  string `json:"category"`
}

// struct to read JSON input
var myReq request 

// Decode the received JSON request to struct
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&myReq)
  if err != nil {
  log.Println( err)
// Handler for invalid JSON received or if you want to decode the request using another struct with int.
  return
}
defer r.Body.Close()

// Convert string to int
numActive, err = strconv.Atoi(myReq.ACTIVE)
if err != nil {
  log.Println(err) 
// Handler for invalid int received
  return
}

// Convert string to int    
numCategory, err = strconv.Atoi(myReq.CATEGORY)
if err != nil {
  log.Println(err)
// Handler for invalid int received
  return
}
deepakssn
  • 5,195
  • 2
  • 24
  • 19
0

I had the same problem with a list of values where the values were string or struct. The solution I'm using is to create a helper struct with fields of expected types and parse value into the correct field.

type Flag struct {
    ID   string `json:"id"`
    Type string `json:"type"`
}

type FlagOrString struct {
    Flag   *Flag
    String *string
}

func (f *FlagOrString) UnmarshalJSON(b []byte) error {
    start := []byte("\"")
    for idx := range start {
        if b[idx] != start[idx] {
            return json.Unmarshal(b, &f.Flag)
        }
    }
    return json.Unmarshal(b, &f.String)
}

var MainStruct struct {
    Vals []FlagOrString
}

Custom Unmarshaller simplifies a code. Personally I prefer this over interface{} as it explicitly states what a developer expects.

Paweł Szczur
  • 5,484
  • 3
  • 29
  • 32