英文:
Unmarshal JSON when only half of the key is known
问题
在Go语言中,可以使用encoding/json
包来解析JSON响应。如果只知道键名的一部分,可以使用结构体的标签来指定JSON字段的名称。
以下是一个示例代码,演示如何在只知道键名一部分的情况下解析JSON响应:
package main
import (
"encoding/json"
"fmt"
)
type Animal struct {
Name string `json:"animal_name"`
Location string `json:"location"`
}
type Person struct {
Name string `json:"person_name"`
Location string `json:"location"`
}
func main() {
data := []byte(`[{"animal_name": "Goofy", "location": "Europe"}, {"person_name": "Gigo", "location": "Asia"}]`)
var animals []Animal
var persons []Person
err := json.Unmarshal(data, &animals)
if err != nil {
err = json.Unmarshal(data, &persons)
if err != nil {
fmt.Println("Failed to unmarshal JSON")
return
}
}
fmt.Println("Animals:")
for _, animal := range animals {
fmt.Println("Name:", animal.Name)
fmt.Println("Location:", animal.Location)
}
fmt.Println("Persons:")
for _, person := range persons {
fmt.Println("Name:", person.Name)
fmt.Println("Location:", person.Location)
}
}
在上面的代码中,我们定义了Animal
和Person
两个结构体,分别对应JSON中的两种可能的对象。通过使用json
标签,我们指定了JSON字段的名称。
然后,我们尝试首先将JSON数据解析为[]Animal
类型的切片,如果解析失败,则尝试将其解析为[]Person
类型的切片。
最后,我们遍历解析后的结果并打印出来。
你可以在这里查看并运行上述代码的在线示例。
英文:
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
?
答案1
得分: 2
你可以将JSON解组为一个映射切片,然后从切片中提取所需的内容:
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将其初始化为映射切片:[]map[string]string。
// 或者,您可以显式设置类型:
// var entries []map[string]string
var entries interface{}
err := json.Unmarshal(data, &entries)
if err != nil {
log.Fatal(err)
}
fmt.Println(entries)
}
英文:
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)
}
答案2
得分: 1
你可以在接口切片上实现json.Unmarshaler
接口。然后在这个自定义函数中使用一些逻辑来确定应该使用哪种结构体类型。我使用json.RawMessage
来避免在我们知道要使用哪种类型之前进行完全的解组。
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 `json:"person_name"`
Location string
}
// Just so we implement Entity
func (p *Person) EntityMarker() {}
type Animal struct {
Name string `json:"animal_name"`
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)
}
英文:
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 `json:"person_name"`
Location string
}
// Just so we implement Entity
func (p *Person) EntityMarker() {}
type Animal struct {
Name string `json:"animal_name"`
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论