Is it possible to unmarshal JSON response when you only know half of the key name?
Example:
data := []byte(`[{"animal_name": "Goofy", "location": "Europe"}, {"person_name": "Gigo", "location": "Asia"}]`)
In this case, there are two possibilities, key name starts with some sort of an identifier (animal, person) and always ends with _key. There can be more (random) identifiers.
In python, you could try to retrieve the key value with endswith or something like that. But is possible to do so in go?
https://go.dev/play/p/HGQ7qFgehve.go
CodePudding user response:
You can unmarshal the JSON to a slice of maps and then extract whatever you need from the slice:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Entry struct {
Animalname string `json:"animal_name"`
Location string
}
func main() {
data := []byte(`[{"animal_name": "Goofy", "location": "Europe"}, {"person_name": "Gigo", "location": "Asia"}]`)
// json.Unmarshal will initialize it as a slice of maps: []map[string]string.
// Alternatively, you can set the type explicitly:
// var entries []map[string]string
var entries interface{}
err := json.Unmarshal(data, &entries)
if err != nil {
log.Fatal(err)
}
fmt.Println(entries)
}
CodePudding user response:
You can implement the json.Unmarshaler interface on a slice of interfaces. Then in this custom function use some logic to determine which struct type should be used. I used json.RawMessage to avoid full unmarshalling of the data until we know which type to use.
package main
import (
"encoding/json"
"strings"
"github.com/davecgh/go-spew/spew"
)
type EntitySlice []Entity
func (es *EntitySlice) UnmarshalJSON(bytes []byte) error {
var objSlice []json.RawMessage
err := json.Unmarshal(bytes, &objSlice)
if err != nil {
return err
}
for _, obj := range objSlice {
kv := make(map[string]json.RawMessage)
err = json.Unmarshal(obj, &kv)
if err != nil {
return err
}
var entityType string
for k := range kv {
i := strings.Index(k, "_name")
if i != -1 {
entityType = k[:i]
break
}
}
var e Entity
switch entityType {
case "person":
e = &Person{}
case "animal":
e = &Animal{}
}
err = json.Unmarshal(obj, &e)
if err != nil {
return err
}
*es = append(*es, e)
}
return nil
}
type Entity interface {
EntityMarker()
}
type Person struct {
Name string
Location string
}
// Just so we implement Entity
func (p *Person) EntityMarker() {}
type Animal struct {
Name string
Location string
}
// Just so we implement Entity
func (a *Animal) EntityMarker() {}
func main() {
data := []byte(`[{"animal_name": "Goofy", "location": "Europe"}, {"person_name": "Gigo", "location": "Asia"}]`)
var entitySlice EntitySlice
err := json.Unmarshal(data, &entitySlice)
if err != nil {
spew.Dump(err)
}
spew.Dump(entitySlice)
}
