golang unmarshal map [string] interface {}到包含带有meta数组的结构的结构

时间:2019-04-29 20:16:46

标签: go unmarshalling

我有以下JSON数据通过API发送。我想将数据解组为下面定义的另一种结构方式。我怎样才能优雅地做到这一点?

{
    "_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
}

1 个答案:

答案 0 :(得分:2)

这可能非常棘手,因为您有一个包含所有内容的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)
}