DEV Community

Cover image for How to Work with Arbitrary JSON in Go?
Mohammad Aziz
Mohammad Aziz

Posted on • Originally published at iaziz786.com

How to Work with Arbitrary JSON in Go?

Abstract

Mostly, when you are working with data you know the schema in which you will receive that data, but there come times when you don't know exactly what will be the structure you will end up receiving. We talk about how you can handle JSON data which are arbitrary in Go.

JSON with a Fixed Structure

When you receive JSON for which you already know the structure, it is pretty easy to work with. You create a struct and unmarshal the bytes to that struct and the JSON package will take care of the rest. Suppose you receive information about today's dinner in JSON and everyday dinner has the following structure.

type Dinner struct {
    MainCourse   string  `json:"main_course"`
    Beverage     string  `json:"beverage"`
    HasDessert   bool    `json:"has_dessert"`
}
Enter fullscreen mode Exit fullscreen mode

And you receive the following JSON value:

food := []byte(`
{
  "main_course": "Biryani",
  "beverage": "Coca Cola",
  "has_dessert": true
}
`)
Enter fullscreen mode Exit fullscreen mode

You just need to create a variable which can hold the value from JSON.

var dinner Dinner
Enter fullscreen mode Exit fullscreen mode

Eventually, decode the values to the dinner variable like this:

json.Unmarshal(food, &dinner)
Enter fullscreen mode Exit fullscreen mode

Now, you can access those values through the fields on the struct. Lucky you, you have dessert for today 😋.

dinner.MainCourse   // Biryani
dinner.HasDessert   // true
Enter fullscreen mode Exit fullscreen mode

JSON That You Are Not Aware

Let's diverge our mind from food to shopping list :). Now assume every weekend you go to purchase some grocery and the items in the list keep changing each week. If you have to represent that into a struct, you got no luck!

Like almost everything in Go where so much randomness is evolved, you have to use interfaces. More particularly an empty interface. You can represent your shopping list like this:

var shoppingList interface{}
Enter fullscreen mode Exit fullscreen mode

Because an empty interface can satisfy any type you can use that to do Unmarshaling.

listJSON := []byte(`
{
  "food": {
    "ketchup": "1pc",
    "noodles": "1pc",
    "rice": "2Kg"
  },
  "utensils": {
    "knives": 2,
    "forks": 3
  }
}
`)

json.Unmarshal(listJSON, &shoppingList)
Enter fullscreen mode Exit fullscreen mode

At this point shoppingList looks like this:

shoppingList = map[string]interface{
    "food": map[string]interface{
        "ketchup": "1pc",
        "noodles": "1pc",
        "rice": "2Kg"
    },
    "utensils": map[string]interface{
        "knives": 2,
        "forks": 3
    }
}
Enter fullscreen mode Exit fullscreen mode

To access the data, you can make a type assertion to map[string]interface{}.

list := shoppingList.(map[string]interface{})
Enter fullscreen mode Exit fullscreen mode

After that, you can access the values like a regular map. But for the values that are nested, you need to do a type assertion again.

utensils := list["utensils"].(map[string]interface{})

utensils["forks"] // 3
Enter fullscreen mode Exit fullscreen mode

Conclusion

You can handle arbitrary JSON with the help of interfaces. You just need to pass an empty interface to the Unmarshal function and the JSON data. After a type assertion, you are good to use the arbitrary fields and their values.

Top comments (1)

Collapse
 
csgeek profile image
csgeek

I would also add those library to your tooling. I've found it very useful. github.com/yalp/jsonpath.