当只知道键的一半时,解析JSON数据。

huangapple go评论86阅读模式
英文:

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)
	}
}

在上面的代码中,我们定义了AnimalPerson两个结构体,分别对应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?

https://go.dev/play/p/HGQ7qFgehve.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来避免在我们知道要使用哪种类型之前进行完全的解组。

(playground链接)

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.

(playground link)

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)
}

huangapple
  • 本文由 发表于 2022年1月4日 00:50:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/70568992.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定