为了账号安全,请及时绑定邮箱和手机立即绑定

惯用的 JSON 对象数组 -> map[string]json.RawMessage

惯用的 JSON 对象数组 -> map[string]json.RawMessage

Go
不负相思意 2022-12-19 20:02:51
我正在尝试解组 HTTP REST API 请求的结果,该请求根据结果的数量返回一个对象或一个对象数组。请求是通用的,因为我试图围绕特定的 REST API 构建一个包装器,它被称为:// Function prototype where I am trying to convert the responsefunc (client *Client) Get(endpoint string, data map[string]string) (map[string]json.RawMessage, error)// The way the function is calledclient.Get("/v1/users", map[string]string{"filter.abc": "lorem ipsum"})两个响应示例:[  {    "abc": "def",    "efg": 123    "hij": [      {        "klm": "nop"      }    ]  },  {    "abc": "def",    "efg": 123    "hij": [      {        "klm": "nop"      }    ]  }]// RESPONSE 1: Array of JSON objects that have child arrays{  "abc": "def",  "efg": 123  "hij": [    {      "klm": "nop"    }  ]}// RESPONSE 2: In this case, only one element was returned.我已经实现了仅针对响应 2 执行此操作,如下所示:// [...]byteBody = ioutil.ReadAll(res.Body)// [...]var body map[string]json.RawMessageif err := json.Unmarshal(byteBody, &body); err != nil { [...] }那么,最惯用的解析方式是什么?有没有办法避免编写冗余代码并解析两个响应?我正在考虑将响应放入的“模型”作为附加参数。这是一个好习惯吗?非常感谢您!
查看完整描述

2 回答

?
湖上湖

TA贡献2003条经验 获得超2个赞

检查 JSON 文档中的第一个非空白字节以确定文档是数组还是对象。


func decodeArrayOrObject(doc []byte) ([]map[string]json.RawMessage, error) {

    trimmedDoc := bytes.TrimSpace(doc)

    switch {

    case len(trimmedDoc) == 0:

        return nil, errors.New("empty body")

    case trimmedDoc[0] == '{':

        var m map[string]json.RawMessage

        err := json.Unmarshal(doc, &m)

        return []map[string]json.RawMessage{m}, err

    case trimmedDoc[0] == '[':

        var s []map[string]json.RawMessage

        err := json.Unmarshal(doc, &s)

        return s, err

    default:

        return nil, errors.New("unexpected type")

    }

}

使用reflect包创建一个适用于任意切片类型的函数:


func decodeArrayOrObject(doc []byte, slicep interface{}) error {

    trimmedDoc := bytes.TrimSpace(doc)

    switch {

    case len(trimmedDoc) == 0:

        return errors.New("empty document")

    case trimmedDoc[0] == '[':

        return json.Unmarshal(doc, slicep)

    case trimmedDoc[0] == '{':

        s := reflect.ValueOf(slicep).Elem()

        s.Set(reflect.MakeSlice(s.Type(), 1, 1))

        return json.Unmarshal(doc, s.Index(0).Addr().Interface())

    default:

        return errors.New("unexpected type")

    }

}

使用指向结构切片的指针或指向结构指针切片的指针调用函数。


var v []Example

err := decodeArrayOrObject(body, &v)


查看完整回答
反对 回复 2022-12-19
?
呼唤远方

TA贡献1856条经验 获得超11个赞

不确定是惯用的,但这段代码可以作为一个例子。


简而言之,您可以尝试以一种格式解组,如果失败,则以另一种格式解组


关键功能是


func parseStr(data string) ([]Item, error) {

    item := Item{}

    if err := json.Unmarshal([]byte(data), &item); err == nil {

        return []Item{item}, nil

    }


    items := []Item{}

    if err := json.Unmarshal([]byte(data), &items); err != nil {

        return nil, errors.New("invalid JSON data")

    }

    return items, nil


}


查看完整回答
反对 回复 2022-12-19
  • 2 回答
  • 0 关注
  • 98 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信