dongqi3533 2019-04-29 20:16
浏览 437
已采纳

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

I have the following json data coming through an API. I want to unmarshal this data into a different way of structure as it is defined below. How can I do it in an elegant way?

{
    "_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条回答 默认 最新

  • dongyimo1293 2019-04-29 21:41
    关注

    This can be quite tricky because you have a json object that holds everything. So i went with the approach of unmarshalling to map of string to *json.RawMessage and then fixing the struct from there.

    To do that you will be using a custom Unmarshaler and the benefit of it is that you delay the actual parsing of the inner messages until you need them.

    So for example if your meta field was wrong or the numbers it said didn't match the length of the map-1 you could exit prematurely.

    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
    ", data)
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 vmware exsi重置后登不上
  • ¥15 易盾点选的cb参数怎么解啊
  • ¥15 MATLAB运行显示错误,如何解决?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题
  • ¥20 yolov5自定义Prune报错,如何解决?
  • ¥15 电磁场的matlab仿真
  • ¥15 mars2d在vue3中的引入问题
  • ¥50 h5唤醒支付宝并跳转至向小荷包转账界面