首页 > 解决方案 > 如何从包含 json 数组的流中一次读取一个 json 对象而不将整个数组加载到内存中?

问题描述

为了减少内存使用并减少延迟,我想在它们全部发送之前开始处理在 http 响应中返回的 json 对象。我正在尝试在我的 Web 服务客户端上编写一个方法,该方法将查询返回一个通道的 Web 服务,该通道将从 Web 请求中提供结构作为脱离流。

    func (c *Client) GetMyObj() (<-chan models.MyObj, <-chan error) {
       outChan := make(chan models.MyObj)
       errChan := make(chan error)
       go func() {
          resp, err := httpClient.Get(c.ServiceUrl, "text/plain;charset=UTF-8", nil)
          if err != nil {
            errChan <- err
            return
          }
          if resp.Status != "200 OK" {
             errChan <- fmt.Errorf("http error: %1", resp.Status)
             return
          }

          dec := json.NewDecoder(resp.Body)

          for dec.More() {
             myO:= models.MyObj{}
             err := dec.Decode(&MyO)
             if err != nil {
                errChan <- err
                return
             }
             outChan <- dfe
          }           
       }()

       return outChan, errChan
    }

不幸的是,当它读取前面的左方括号时会产生异常。 json 文档看起来如下所示,但数组中有更多对象,对象上有更多属性,但你明白了:

    [{"name":"Value"}
    ,{"name":"Value"}
    ,{"name":"Value"}]

标签: jsongostream

解决方案


json.Decoder.Decode(...)方法文档中列出的示例正好说明了这一点。

简而言之,您执行以下步骤:

  1. dec.Token()使用(并且可选地期望它是一个开放的方括号)读取一个标记
  2. dec.Decode()使用while读取令牌dec.More()
  3. 或者,阅读最后的右方括号。

例如(去游乐场):

dec := json.NewDecoder(jsonStream)
type Item struct{ Name string }

// Read the open bracket.
t, err := dec.Token()
if err != nil {
    panic(err)
}
fmt.Printf("OK: %T: %v\n", t, t)

// While the array contains values.
for dec.More() {
    // Decode an array value.
    var item Item
    err := dec.Decode(&item)
    if err != nil {
        panic(err)
    }
    fmt.Printf("OK: item=%#v\n", item)
}

// Read the closing bracket.
t, err = dec.Token()
if err != nil {
    panic(err)
}
fmt.Printf("OK: %T: %v\n", t, t)
// OK: json.Delim: [
// OK: item=main.Item{Name:"Value1"}
// OK: item=main.Item{Name:"Value2"}
// OK: item=main.Item{Name:"Value3"}
// OK: json.Delim: ]

推荐阅读