golang 将 map[string]interface{} 解码为包含元数组的结构

标签 go unmarshalling

我有以下来自 API 的 json 数据。我想将这些数据解码为下面定义的不同结构方式。我怎样才能优雅地做到这一点?

{
    "_meta": {
        "count": 2,
        "total": 2
    },
    "0": {
        "key": "key0",
        "name": "name0"
    },
    "1": {
        "key": "key1",
        "name": "name1"
    },
    "2": {
        "key": "key2",
        "name": "name2"
    }
    // It goes on..
}
type Data struct {
   Meta Meta `json:"_meta,omitempty"`
   Elements []Element
}

type Element struct {
   Key string
   Name string
}

type Meta struct{
   Count int
   Total int
}

最佳答案

这可能非常棘手,因为您有一个包含所有内容的 json 对象。所以我采用了解码将字符串映射到 *json.RawMessage 然后从那里修复结构的方法。

要做到这一点,您将使用自定义 Unmarshaler,它的好处是您可以延迟内部消息的实际解析,直到您需要它们为止。

因此,例如,如果您的元字段错误或它表示的数字与 map-1 的长度不匹配,您可能会过早退出。

package main

import (
        "encoding/json"
        "fmt"
)

type jdata map[string]*json.RawMessage

type data struct {
        Meta     Meta
        Elements []Element
}

//Element is a key val assoc
type Element struct {
        Key  string
        Name string
}

//Meta holds counts and total of elems
type Meta struct {
        Count int
        Total int
}

var datain = []byte(`
{
    "_meta": {
        "count": 2,
        "total": 2
    },
    "0": {
        "key": "key0",
        "name": "name0"
    },
    "1": {
        "key": "key1",
        "name": "name1"
    },
    "2": {
        "key": "key2",
        "name": "name2"
    }
}`)

func (d *data) UnmarshalJSON(buf []byte) (err error) {
        var (
                meta *json.RawMessage
                ok   bool
        )
        jdata := new(jdata)

        if err = json.Unmarshal(buf, jdata); err != nil {
                return
        }
        if meta, ok = (*jdata)["_meta"]; !ok {
                return fmt.Errorf("_meta field not found in JSON")
        }
        if err = json.Unmarshal(*meta, &d.Meta); err != nil {
                return
        }
        for k, v := range *jdata {
                if k == "_meta" {
                        continue
                }
                elem := &Element{}
                if err = json.Unmarshal(*v, elem); err != nil {
                        return err
                }
                d.Elements = append(d.Elements, *elem)
        }
        return nil

}

func main() {
        data := &data{}
        if err := data.UnmarshalJSON(datain); err != nil {
                panic(err)
        }
        fmt.Printf("decoded:%v\n", data)
}

关于golang 将 map[string]interface{} 解码为包含元数组的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55910252/

相关文章:

go - slice 文字和make slice之间在行为上有区别吗?

xml - 解码嵌套在 XML 中的 HTML

java - JAXB 包装所有字段

java - UnmarshalException 错误

java - 将 XML 属性解码为对象值

java - 如何使用 jaxb 将 XML 数据添加到另一个数据并将它们存储在单个文件中?

go - 使用 channel 限制活跃的 go routines 数量

Azure SDK for Go - 为什么有多个 KeyVault 和 blob 存储客户端?

go - golang中的深度复制数据结构

amazon-web-services - 使用 golang 的 AWS S3 并行下载