ChatGPT解决这个技术问题 Extra ChatGPT

使用 json.Unmarshal 与 json.NewDecoder.Decode 解码 JSON

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

我已经从几个库中阅读了源代码,从我所见,我基本上有两种编码和解码 JSON 字符串的可能性。

使用 json.Unmarshal 传递整个响应字符串

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 响应时,第二个版本似乎需要更少的代码,但由于我已经看到了这两个版本,我想知道是否有任何偏好是否应该使用解决方案而不是其他解决方案。

此外,the accepted answer from this question

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

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

pull request on GitHub 用 json.NewDecoder 替换了对 Unmarshal 的调用,以“删除 JSON 解码中的缓冲区”。
这仅取决于您使用哪种输入更方便。 blog.golang.org/json-and-go 给出了使用这两种技术的示例。
IMO,ioutil.ReadAll几乎总是做错事。它与您的目标无关,但需要您有足够的连续内存来存储管道中可能出现的任何内容,即使最后 20TB 的响应在 JSON 中的最后一个 } 之后也是如此。
@Dustin 您可以使用 io.LimitReader 来防止这种情况。
@Dustin“ReadAll 几乎总是错误的”我认为如果 JSON 很小并且大约只有几 Kb,那么最好将其全部阅读以释放连接。这很常见,所以它看起来像 ReadAll 然后 Unmarshall 在大多数情况下是更好的解决方案

S
Seth

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

所以一个更好的经验法则是:

如果您的数据来自 io.Reader 流,或者您需要从数据流中解码多个值,请使用 json.Decoder。

如果内存中已有 JSON 数据,请使用 json.Unmarshal。

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


另外:通过检查 Go 1.3 源代码,我们还可以了解到,对于编码,如果您使用 json.Encoder,它将重用一个全局缓冲池(由新的 sync.Pool 支持),这应该会大大减少缓冲流失如果您要编码很多json。只有一个全局池,所以不同的 json.Encoder 共享它。 json.Marshal 接口无法做到这一点的原因是字节被返回给用户,而用户没有办法将字节“返回”到池中。因此,如果您要进行大量编码,则 json.Marshal 总是有相当多的缓冲区流失。
@Flimzy:你确定吗?源代码仍然说它在解码之前将整个值读入缓冲区:github.com/golang/go/blob/master/src/encoding/json/…Buffered 方法可让您查看在值之后读入内部缓冲区的任何额外数据。
@JamesHenstridge:不,你可能是对的。我只是对您的陈述的解释与您的预期不同。为混乱道歉。
S
Samir Kape

我在 Go web 编程书中找到了这一段。但是没有给出解释

那么我们什么时候使用 Decoder 和 Unmarshal 呢?

这取决于输入。如果您的数据来自 io.Reader 流,例如 http.Request 的正文,请使用解码器。如果您在字符串或内存某处有数据,请使用 Unmarshal。