处理HTTP响应中的接口的最佳方法

山姆·雷诺兹(Sam Reynolds):

我正在使用以这种方式格式化其响应的API:

{
  "err": 0,
  "data": **Other json structure**
}

我现在得到响应的方式是将响应放入这样的结构中:

type Response struct {
   Err int        `json:"err"`
   Data interface{} `json:"data"`
}

然后我得到回应后就这样做了

jsonbytes, _ := json.Marshal(resp.Data)
json.Unmarshal(jsonBytes, &dataStruct)

我只是忽略此示例中的错误。
当我知道数据的外观和类型时,我在编组和拆组似乎有点怪异。

有没有我没有看到的更简单的解决方案,或者这是正常的事情?

编辑:我可能应该提到响应对象中的Data属性可以根据我正在执行的API调用而有所不同。

jsageryd:

JSON解组器使用反射来查看要解组的类型。给定未初始化的数据interface{}作为编组数据的目的地,则JSON object将被解组到JSON中map[string]interface{}例如,在Playground中)。

这里有一些想法。

选项A

如果知道数据类型,则可以为每种类型定义新的响应结构。例:

type FooResponse struct {
  Err  int `json:"err"`
  Data Foo `json:"data"`
}

type Foo struct {
  FooField string `json:"foofield"`
}

type BarResponse struct {
  Err  int `json:"err"`
  Data Bar `json:"data"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

选项B

如果您希望使用单个Response结构而不是每个类型一个,可以data使用json.RawMessage数据类型告诉JSON解组器避免对字段进行解组,直到以后

package main

import (
  "encoding/json"
  "fmt"
  "log"
)

type Response struct {
  Err  int             `json:"err"`
  Data json.RawMessage `json:"data"`
}

type Foo struct {
  FooField string `json:"foofield"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

func main() {
  fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
  barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)

  var (
    resp Response
    foo  Foo
    bar  Bar
  )

  // Foo
  if err := json.Unmarshal(fooRespJSON, &resp); err != nil {
    log.Fatal(err)
  }
  if err := json.Unmarshal(resp.Data, &foo); err != nil {
    log.Fatal(err)
  }
  fmt.Println("foo:", foo)

  // Bar
  if err := json.Unmarshal(barRespJSON, &resp); err != nil {
    log.Fatal(err)
  }
  if err := json.Unmarshal(resp.Data, &bar); err != nil {
    log.Fatal(err)
  }
  fmt.Println("bar:", bar)
}

输出:

foo: {foo value}
bar: {bar value}

https://play.golang.org/p/Y7D4uhaC4a8

选项C

@mkopriva在对该问题的评论中指出,第三个选项是interface{}用作中介数据类型并将其预先初始化为已知数据类型。

重点放在中介这个词上-当然interface{}最好避免绕过Rob Pike的Go Proverbs)。这里的用例是允许使用任何数据类型而无需多个不同的Response类型。避免暴露的方法interface{}是完全包装响应,仅暴露数据和错误:

package main

import (
  "encoding/json"
  "fmt"
  "log"
)

type Foo struct {
  FooField string `json:"foofield"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

type Error struct {
  Code int
}

func (e *Error) Error() string {
  return fmt.Sprintf("error code %d", e.Code)
}

func unmarshalResponse(data []byte, v interface{}) error {
  resp := struct {
    Err  int         `json:"err"`
    Data interface{} `json:"data"`
  }{Data: v}

  if err := json.Unmarshal(data, &resp); err != nil {
    return err
  }

  if resp.Err != 0 {
    return &Error{Code: resp.Err}
  }

  return nil
}

func main() {
  fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
  barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)
  errRespJSON := []byte(`{"err": 123}`)

  // Foo
  var foo Foo
  if err := unmarshalResponse(fooRespJSON, &foo); err != nil {
    log.Fatal(err)
  }
  fmt.Println("foo:", foo)

  // Bar
  var bar Bar
  if err := unmarshalResponse(barRespJSON, &bar); err != nil {
    log.Fatal(err)
  }
  fmt.Println("bar:", bar)

  // Error response
  var v interface{}
  if err := unmarshalResponse(errRespJSON, &v); err != nil {
    log.Fatal(err)
  }
}

输出:

foo: {foo value}
bar: {bar value}
2009/11/10 23:00:00 error code 123

https://play.golang.org/p/5SVfQGwS-Wy

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章