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


199

我正在开发一个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)

就我而言,在处理实现的HTTP响应时,io.Reader第二个版本似乎需要较少的代码,但是由于我已经看到了两者,所以我想知道是否有任何偏好是应该使用解决方案,而不是使用其他解决方案。

此外,从这个问题接受的答案

请使用json.Decoder代替json.Unmarshal

但没有提及原因。我应该避免使用json.Unmarshal吗?


在GitHub拉动请求更换,以解组与json.NewDecoder调用“中JSON解码删除缓冲器”。
马特

这取决于哪种输入更方便您使用。blog.golang.org/json-and-go提供了使用这两种技术的示例。
rexposadas 2014年

15
国际海事组织,ioutil.ReadAll几乎总是错误的做法。它与您的目标无关,但是要求您有足够的连续内存来存储管道中可能发生的所有事件,即使最后20TB的响应}位于JSON中的最后一个之后。
达斯汀2014年

@Dustin您可以io.LimitReader用来防止这种情况。
伊南克·古姆斯

Answers:


238

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

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

  • 使用json.Decoder,如果你的数据从一个即将io.Reader流,或者需要多个值,从数据流进行解码。
  • 使用json.Unmarshal如果你已经在内存中的JSON数据。

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


25
另外:通过检查Go 1.3源代码,我们还可以了解到,如果使用json.Encoder进行编码,它将重用全局缓冲池(由新的sync.Pool支持),这将大大减少缓冲区流失如果您要编码很多json。只有一个全局池,所以json.Encoder是不同的共享它。不能对json.Marshal接口执行此操作的原因是因为字节已返回给用户,并且用户没有办法将字节“返回”到池中。因此,如果您要进行大量编码,则json.Marshal总是会有很多缓冲区中断。
阿克套2014年

@Flimzy:确定吗?源代码仍然说它在解码之前将整个值读入缓冲区:github.com/golang/go/blob/master/src/encoding/json/…。该Buffered方法可以让您看到在该值之后读入内部缓冲区的任何其他数据。
James Henstridge

@JamesHenstridge:不,你可能是对的。我对您的陈述的解释与您的预期有所不同。抱歉造成混乱。
Flimzy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.