英文:
Unmarshal JSON object into slice of structs with key and value
问题
我正在学习GO,并且被以下问题困扰:
我收到一个JSON字符串,我想在GO中进行解组。
JSON的格式如下:
{
  "MAINKEY": {
    "key1": 1,
    "key2": [1, 2]
  }
}
我只对MAINKEY的内容感兴趣,但是与这个问题类似,我不知道键的名称,这些键应该反映在映射的名称上。
最终,我想要以下对象:
type Result struct {
	Key   string
	Value []int
}
expectedResult := []Result{
	{"key1", []int{1}},
	{"key2", []int{1, 2}},
}
fmt.Printf("WANT: %+v\n", expectedResult)
//> WANT: [{Key:key1 Value:[1]} {Key:key2 Value:[1 2]}]
如果可能的话,我不想先解组为map[string]interface{}(但如果没有其他方法,那也可以)。
到目前为止,完整的代码如下:
package main
import (
	"encoding/json"
	"fmt"
)
// does produce expected result at the moment...
type Result struct {
	Key   string
	Value []int
}
type Outer struct {
	Key Result `json:"MAINKEY"`
}
func main() {
	input := `{"MAINKEY": {"key1": 1, "key2": [1, 2]}}`
	var cont Outer
	json.Unmarshal([]byte(input), &cont)
	fmt.Printf("GOT: %+v\n", cont)
	expectedResult := []Result{
		{"key1", []int{1}},
		{"key2", []int{1, 2}},
	}
	fmt.Printf("WANT: %+v\n", expectedResult)
}
英文:
I am still learning GO and I am stumped by the following problem:
I receive a JSON string that I want to unmarshal in GO.
The JSON looks like this
{
  "MAINKEY": {
    "key1": 1,
    "key2": [1, 2]
  }
}
I am only interested in the contents of the MAINKEY, but similar to this question, I do not know the names of the keys, which should reflect the names of the map.
In the end, I want to have the following object:
type Result struct {
	Key   string
	Value []int
}
expectedResult := []Result{
	{"key1", []int{1}},
	{"key2", []int{1, 2}},
}
fmt.Printf("WANT: %+v\n", expectedResult)
//> WANT: [{Key:key1 Value:[1]} {Key:key2 Value:[1 2]}]
If possible, I don't want to unmarshal into a map[string]interface{} first (but if there is no other way, that would be ok too).
Full code so far is:
package main
import (
	"encoding/json"
	"fmt"
)
// does produce expected result at the moment...
type Result struct {
	Key   string
	Value []int
}
type Outer struct {
	Key Result `json:"MAINKEY"`
}
func main() {
	input := `{"MAINKEY": {"key1": 1, "key2": [1, 2]}}`
	var cont Outer
	json.Unmarshal([]byte(input), &cont)
	fmt.Printf("GOT: %+v\n", cont)
	expectedResult := []Result{
		{"key1", []int{1}},
		{"key2", []int{1, 2}},
	}
	fmt.Printf("WANT: %+v\n", expectedResult)
}
答案1
得分: 3
你可以使用自定义的解组器(unmarshaler)来处理 map 类型:
type ResultList []Result
func (ls *ResultList) UnmarshalJSON(data []byte) error {
	var obj map[string]json.RawMessage
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	for key, raw := range obj {
		r := Result{Key: key}
		if raw[0] == '[' { // 假设是 int 数组
			if err := json.Unmarshal(raw, &r.Value); err != nil {
				return err
			}
		} else { // 假设是单个 int
			var i int
			if err := json.Unmarshal(raw, &i); err != nil {
				return err
			}
			r.Value = append(r.Value, i)
		}
		*ls = append(*ls, r)
	}
	return nil
}
如果你需要保留顺序,你可以对输入进行分词:
type ResultList []Result
func (ls *ResultList) UnmarshalJSON(data []byte) error {
	d := json.NewDecoder(bytes.NewReader(data))
	i := -1
	for {
		t, err := d.Token()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		switch v := t.(type) {
		case string:
			*ls = append(*ls, Result{Key: v})
			i += 1
		case float64:
			(*ls)[i].Value = append((*ls)[i].Value, int(v))
		}
	}
	return nil
}
英文:
You can use a custom unmarshaler with a map:
type ResultList []Result
func (ls *ResultList) UnmarshalJSON(data []byte) error {
	var obj map[string]json.RawMessage
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	for key, raw := range obj {
		r := Result{Key: key}
		if raw[0] == '[' { // assume array of ints
			if err := json.Unmarshal(raw, &r.Value); err != nil {
				return err
			}
		} else { // assume single int
			var i int
			if err := json.Unmarshal(raw, &i); err != nil {
				return err
			}
			r.Value = append(r.Value, i)
		}
		*ls = append(*ls, r)
	}
	return nil
}
https://go.dev/play/p/Epd6cLwyWUm
Or, if you need to retain the order, you can tokenize the input:
type ResultList []Result
func (ls *ResultList) UnmarshalJSON(data []byte) error {
	d := json.NewDecoder(bytes.NewReader(data))
	i := -1
	for {
		t, err := d.Token()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		switch v := t.(type) {
		case string:
			*ls = append(*ls, Result{Key: v})
			i += 1
		case float64:
			(*ls)[i].Value = append((*ls)[i].Value, int(v))
		}
	}
	return nil
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论