Skip to content

Instantly share code, notes, and snippets.

@mjohnsullivan
Created December 14, 2015 23:17
Show Gist options
  • Star 58 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save mjohnsullivan/24647cae50928a34b5cc to your computer and use it in GitHub Desktop.
Save mjohnsullivan/24647cae50928a34b5cc to your computer and use it in GitHub Desktop.
Parse JSON objects with arbitrary key names in Go using interfaces and type assertions
// Parsing arbitrary JSON using interfaces in Go
// Demonstrates how to parse JSON with abritrary key names
// See https://blog.golang.org/json-and-go for more info on generic JSON parsing
package main
import (
"encoding/json"
"fmt"
)
var jsonBytes = []byte(`
{
"key1":{
"Item1": "Value1",
"Item2": 1},
"key2":{
"Item1": "Value2",
"Item2": 2},
"key3":{
"Item1": "Value3",
"Item2": 3}
}`)
// Item struct; we want to create these from the JSON above
type Item struct {
Item1 string
Item2 int
}
// Implement the String interface for pretty printing Items
func (i Item) String() string {
return fmt.Sprintf("Item: %s, %d", i.Item1, i.Item2)
}
func main() {
// Unmarshal using a generic interface
var f interface{}
err := json.Unmarshal(jsonBytes, &f)
if err != nil {
fmt.Println("Error parsing JSON: ", err)
}
// JSON object parses into a map with string keys
itemsMap := f.(map[string]interface{})
// Loop through the Items; we're not interested in the key, just the values
for _, v := range itemsMap {
// Use type assertions to ensure that the value's a JSON object
switch jsonObj := v.(type) {
// The value is an Item, represented as a generic interface
case interface{}:
var item Item
// Access the values in the JSON object and place them in an Item
for itemKey, itemValue := range jsonObj.(map[string]interface{}) {
switch itemKey {
case "Item1":
// Make sure that Item1 is a string
switch itemValue := itemValue.(type) {
case string:
item.Item1 = itemValue
default:
fmt.Println("Incorrect type for", itemKey)
}
case "Item2":
// Make sure that Item2 is a number; all numbers are transformed to float64
switch itemValue := itemValue.(type) {
case float64:
item.Item2 = int(itemValue)
default:
fmt.Println("Incorrect type for", itemKey)
}
default:
fmt.Println("Unknown key for Item found in JSON")
}
}
fmt.Println(item)
// Not a JSON object; handle the error
default:
fmt.Println("Expecting a JSON object; got something else")
}
}
}
@infinityCounter
Copy link

I found this an incredibly effective solution in parsing JSON objects with a variable number of top level keys in Golang.

@ankit-sahu
Copy link

Thank you, helped me in solving one of my problem.

@mt-inside
Copy link

Beautiful!

@wjjmjh
Copy link

wjjmjh commented Aug 2, 2021

although its 6 years ago, I found its still useful, oolala

@arunx2
Copy link

arunx2 commented Jan 25, 2023

Beautiful! Thanks for sharing.

@leijurv
Copy link

leijurv commented Sep 6, 2023

shouldn't line 53 be case map[string]interface{}:, then line 56 can just be for itemKey, itemValue := range jsonObj {? 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment