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

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

Unmarshal JSON object into slice of structs with key and value

问题

我正在学习GO,并且被以下问题困扰:
我收到一个JSON字符串,我想在GO中进行解组。
JSON的格式如下:

  1. {
  2. "MAINKEY": {
  3. "key1": 1,
  4. "key2": [1, 2]
  5. }
  6. }

我只对MAINKEY的内容感兴趣,但是与这个问题类似,我不知道键的名称,这些键应该反映在映射的名称上。

最终,我想要以下对象:

  1. type Result struct {
  2. Key string
  3. Value []int
  4. }
  5. expectedResult := []Result{
  6. {"key1", []int{1}},
  7. {"key2", []int{1, 2}},
  8. }
  9. fmt.Printf("WANT: %+v\n", expectedResult)
  10. //> WANT: [{Key:key1 Value:[1]} {Key:key2 Value:[1 2]}]

如果可能的话,我不想先解组为map[string]interface{}(但如果没有其他方法,那也可以)。

到目前为止,完整的代码如下:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. // does produce expected result at the moment...
  7. type Result struct {
  8. Key string
  9. Value []int
  10. }
  11. type Outer struct {
  12. Key Result `json:"MAINKEY"`
  13. }
  14. func main() {
  15. input := `{"MAINKEY": {"key1": 1, "key2": [1, 2]}}`
  16. var cont Outer
  17. json.Unmarshal([]byte(input), &cont)
  18. fmt.Printf("GOT: %+v\n", cont)
  19. expectedResult := []Result{
  20. {"key1", []int{1}},
  21. {"key2", []int{1, 2}},
  22. }
  23. fmt.Printf("WANT: %+v\n", expectedResult)
  24. }
英文:

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

  1. {
  2. "MAINKEY": {
  3. "key1": 1,
  4. "key2": [1, 2]
  5. }
  6. }

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:

  1. type Result struct {
  2. Key string
  3. Value []int
  4. }
  5. expectedResult := []Result{
  6. {"key1", []int{1}},
  7. {"key2", []int{1, 2}},
  8. }
  9. fmt.Printf("WANT: %+v\n", expectedResult)
  10. //> 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:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. // does produce expected result at the moment...
  7. type Result struct {
  8. Key string
  9. Value []int
  10. }
  11. type Outer struct {
  12. Key Result `json:"MAINKEY"`
  13. }
  14. func main() {
  15. input := `{"MAINKEY": {"key1": 1, "key2": [1, 2]}}`
  16. var cont Outer
  17. json.Unmarshal([]byte(input), &cont)
  18. fmt.Printf("GOT: %+v\n", cont)
  19. expectedResult := []Result{
  20. {"key1", []int{1}},
  21. {"key2", []int{1, 2}},
  22. }
  23. fmt.Printf("WANT: %+v\n", expectedResult)
  24. }

答案1

得分: 3

你可以使用自定义的解组器(unmarshaler)来处理 map 类型:

  1. type ResultList []Result
  2. func (ls *ResultList) UnmarshalJSON(data []byte) error {
  3. var obj map[string]json.RawMessage
  4. if err := json.Unmarshal(data, &obj); err != nil {
  5. return err
  6. }
  7. for key, raw := range obj {
  8. r := Result{Key: key}
  9. if raw[0] == '[' { // 假设是 int 数组
  10. if err := json.Unmarshal(raw, &r.Value); err != nil {
  11. return err
  12. }
  13. } else { // 假设是单个 int
  14. var i int
  15. if err := json.Unmarshal(raw, &i); err != nil {
  16. return err
  17. }
  18. r.Value = append(r.Value, i)
  19. }
  20. *ls = append(*ls, r)
  21. }
  22. return nil
  23. }

如果你需要保留顺序,你可以对输入进行分词:

  1. type ResultList []Result
  2. func (ls *ResultList) UnmarshalJSON(data []byte) error {
  3. d := json.NewDecoder(bytes.NewReader(data))
  4. i := -1
  5. for {
  6. t, err := d.Token()
  7. if err == io.EOF {
  8. break
  9. }
  10. if err != nil {
  11. return err
  12. }
  13. switch v := t.(type) {
  14. case string:
  15. *ls = append(*ls, Result{Key: v})
  16. i += 1
  17. case float64:
  18. (*ls)[i].Value = append((*ls)[i].Value, int(v))
  19. }
  20. }
  21. return nil
  22. }
英文:

You can use a custom unmarshaler with a map:

  1. type ResultList []Result
  2. func (ls *ResultList) UnmarshalJSON(data []byte) error {
  3. var obj map[string]json.RawMessage
  4. if err := json.Unmarshal(data, &obj); err != nil {
  5. return err
  6. }
  7. for key, raw := range obj {
  8. r := Result{Key: key}
  9. if raw[0] == '[' { // assume array of ints
  10. if err := json.Unmarshal(raw, &r.Value); err != nil {
  11. return err
  12. }
  13. } else { // assume single int
  14. var i int
  15. if err := json.Unmarshal(raw, &i); err != nil {
  16. return err
  17. }
  18. r.Value = append(r.Value, i)
  19. }
  20. *ls = append(*ls, r)
  21. }
  22. return nil
  23. }

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


Or, if you need to retain the order, you can tokenize the input:

  1. type ResultList []Result
  2. func (ls *ResultList) UnmarshalJSON(data []byte) error {
  3. d := json.NewDecoder(bytes.NewReader(data))
  4. i := -1
  5. for {
  6. t, err := d.Token()
  7. if err == io.EOF {
  8. break
  9. }
  10. if err != nil {
  11. return err
  12. }
  13. switch v := t.(type) {
  14. case string:
  15. *ls = append(*ls, Result{Key: v})
  16. i += 1
  17. case float64:
  18. (*ls)[i].Value = append((*ls)[i].Value, int(v))
  19. }
  20. }
  21. return nil
  22. }

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:

确定