使用JSON.Unmarshal vs JSON.NewDecoder.Decode解码JSON

使用JSON.Unmarshal vs JSON.NewDecoder.Decode解码JSON,json,go,Json,Go,我正在开发一个API客户机,需要根据请求对JSON有效负载进行编码,并从响应中解码JSON主体 我已经阅读了几个库中的源代码,根据我所看到的,我基本上有两种可能对JSON字符串进行编码和解码 使用传递整个响应字符串 data, err := ioutil.ReadAll(resp.Body) if err == nil && data != nil { err = json.Unmarshal(data, value) } 或者使用json.NewDecoder.Dec

我正在开发一个API客户机,需要根据请求对JSON有效负载进行编码,并从响应中解码JSON主体

我已经阅读了几个库中的源代码,根据我所看到的,我基本上有两种可能对JSON字符串进行编码和解码

使用传递整个响应字符串

data, err := ioutil.ReadAll(resp.Body)
if err == nil && data != nil {
    err = json.Unmarshal(data, value)
}
或者使用
json.NewDecoder.Decode

err = json.NewDecoder(resp.Body).Decode(value)
在我的例子中,当处理实现
io.Reader
的HTTP响应时,第二个版本似乎需要更少的代码,但由于我已经看到了这两个版本,我想知道是否有任何偏好,我是否应该使用一个解决方案而不是另一个

此外,他说

请使用
json.Decoder
而不是
json.Unmarshal


但它没有提到原因。我真的应该避免使用json.Unmarshal吗?

这取决于你的输入是什么。如果查看
json.Decoder
方法的
Decode
实现,它会在内存中缓冲整个json值,然后再将其解组为Go值。因此,在大多数情况下,它的内存效率不会提高(尽管在未来的语言版本中,这很容易改变)

因此,更好的经验法则是:

  • 如果您的数据来自
    io.Reader
    流,或者您需要对数据流中的多个值进行解码,请使用
    json.Decoder
  • 如果内存中已经有json数据,请使用
    json.Unmarshal

对于从HTTP请求中读取的情况,我会选择
json.Decoder
,因为您显然是从流中读取的。

这将用json.NewDecoder替换对Unmarshal的调用,以“在json解码中删除缓冲区”。这取决于什么输入更方便您使用。给出了使用这两种技术的示例。IMO,
ioutil.ReadAll
几乎总是错误的做法。它与您的目标无关,但要求您有足够的连续内存来存储管道中可能出现的任何内容,即使最后20 TB的响应在JSON中最后一个
}
之后。@Dustin您可以使用
io.LimitReader
来防止这种情况。另外:通过检查Go 1.3源代码,我们还可以了解到,对于编码,如果您使用json.Encoder,它将重用一个全局缓冲池(由新的sync.pool支持),如果您对大量json进行编码,这将大大减少缓冲区搅动。只有一个全局池,不同的json.Encoder共享它。无法对json.Marshal接口执行此操作的原因是,字节会返回给用户,而用户无法将字节“返回”到池中。所以,如果你在做大量的编码,json.Marshal总是有相当多的缓冲区搅动。@Flimzy:你确定吗?源代码仍然表示它在解码之前会将整个值读入缓冲区:。
Buffered
方法允许您查看在值之后读入内部缓冲区的任何额外数据。@JamesHenstridge:不,您可能是对的。我只是把你的话解释得和你想的不一样。为这一混乱道歉。