Common daily task
Go provides has a json package in the standard library. However a common case we face everyday at work is that our input is not consistent, that means a json entry could have different data types.
Consider the following input jsons
# good input
{
"name": "Sam smith",
"age" : 34
}
# lazy input
{
"name": "Sam smith stringer",
"age" : "41"
}
as one can see the "age" field has two different types string and number.
Go provides a trick to handle this case; So the recipe is
- Write your go struct with the right types
- Create an Alias for your struct to avoid recursively unmarshal
- Unmarshal to your intermediate struct with your embbeded alias
- Select the attributes of your struct and parse them to the intended type
Full code snippet
package main
import (
"fmt"
"encoding/json"
"strconv"
)
type InputJSON struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
}
/* Alias to avoid calling implicit UnmarshalJSON of your original
struct
*/
type Alias InputJSON
/* write your Unmarshal function using an intermediate struct to use interface to handle any type */
func (i *InputJSON) UnmarshalJSON(input []byte) error {
// intermediate step
middle := struct {
Alias
Age interface{} `json:"age,omitempty"`
}{}
if err := json.Unmarshal(input, &middle); err != nil {
return err
}
*i = InputJSON(middle.Alias)
if middle.Age != nil {
switch realValue := (middle.Age).(type) {
case string:
intValue, err := strconv.Atoi(realValue)
if err != nil {
return err
}
i.Age = intValue
case float64:
// by default uses a float64 for number representation
i.Age = int(realValue)
}
}
return nil
}
func main() {
// use cases
expectedCase := []byte(`
{
"name": "Sam smith",
"age" : 34
}
`)
awfulCase := []byte(`
{
"name": "Sam smith stringer",
"age" : "41"
}
`)
goodInput := InputJSON{}
if err := json.Unmarshal(expectedCase, &goodInput); err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", goodInput)
badInput := InputJSON{}
if err := json.Unmarshal(awfulCase, &badInput); err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", badInput)
}
Not the most elegant way to do it, but it works :)
Top comments (0)