英文:
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论