将JSON对象解组成具有键和值的结构体切片。

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

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
}

https://go.dev/play/p/nABjw5IHZ7R

huangapple
  • 本文由 发表于 2022年11月11日 22:20:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/74403870.html
匿名

发表评论

匿名网友

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

确定