Giải mã JSON bằng json.Unmarshal vs json.NewDecoder.Decode


200

Tôi đang phát triển ứng dụng khách API nơi tôi cần mã hóa tải trọng JSON theo yêu cầu và giải mã phần thân JSON từ phản hồi.

Tôi đã đọc mã nguồn từ một số thư viện và từ những gì tôi đã thấy, về cơ bản tôi có hai khả năng mã hóa và giải mã chuỗi JSON.

Sử dụng json.Unmarshalchuyển toàn bộ chuỗi phản hồi

data, err := ioutil.ReadAll(resp.Body)
if err == nil && data != nil {
    err = json.Unmarshal(data, value)
}

hoặc sử dụng json.NewDecoder.Decode

err = json.NewDecoder(resp.Body).Decode(value)

Trong trường hợp của tôi, khi xử lý các phản hồi HTTP thực hiện io.Reader, phiên bản thứ hai dường như yêu cầu ít mã hơn, nhưng vì tôi đã thấy cả hai tôi tự hỏi liệu tôi có nên sử dụng giải pháp nào hơn là giải pháp khác không.

Hơn nữa, câu trả lời được chấp nhận từ câu hỏi này nói

Vui lòng sử dụng json.Decoderthay vì json.Unmarshal.

nhưng nó không đề cập đến lý do. Tôi có nên thực sự tránh sử dụng json.Unmarshal?


Yêu cầu kéo này trên GitHub đã thay thế một cuộc gọi đến Unmarshal bằng json.NewDecoder để "xóa bộ đệm trong giải mã JSON."
Matt

Nó chỉ phụ thuộc vào những gì đầu vào thuận tiện hơn cho bạn sử dụng. blog.golang.org/json-and-go đưa ra ví dụ về việc sử dụng cả hai kỹ thuật.
rexposeadas

15
IMO, ioutil.ReadAllhầu như lúc nào cũng là điều sai trái để làm. Nó không liên quan đến mục tiêu của bạn, nhưng yêu cầu bạn phải có đủ bộ nhớ liền kề để lưu trữ bất cứ thứ gì có thể rơi xuống đường ống, ngay cả khi 20TB phản hồi cuối cùng là sau lần cuối cùng }trong JSON của bạn.
Dustin

@Dustin Bạn có thể sử dụng io.LimitReaderđể ngăn chặn điều đó.
Inanc Gumus

Câu trả lời:


238

Nó thực sự phụ thuộc vào đầu vào của bạn là gì. Nếu bạn nhìn vào việc thực hiện Decodephương thức json.Decoder, nó sẽ đệm toàn bộ giá trị JSON trong bộ nhớ trước khi sắp xếp nó thành giá trị Go. Vì vậy, trong hầu hết các trường hợp, nó sẽ không còn hiệu quả về bộ nhớ nữa (mặc dù điều này có thể dễ dàng thay đổi trong phiên bản ngôn ngữ trong tương lai).

Vì vậy, một quy tắc tốt hơn là:

  • Sử dụng json.Decodernếu dữ liệu của bạn đến từ một io.Readerluồng hoặc bạn cần giải mã nhiều giá trị từ một luồng dữ liệu.
  • Sử dụng json.Unmarshalnếu bạn đã có dữ liệu JSON trong bộ nhớ.

Đối với trường hợp đọc từ yêu cầu HTTP, tôi sẽ chọn json.Decodervì rõ ràng bạn đang đọc từ một luồng.


25
Ngoài ra: bằng cách kiểm tra mã nguồn Go 1.3, chúng ta cũng có thể biết rằng để mã hóa, nếu bạn sử dụng json.Encoder, nó sẽ sử dụng lại nhóm bộ đệm toàn cầu (được hỗ trợ bởi sync.Pool mới), điều này sẽ làm giảm rất nhiều bộ đệm nếu bạn đang mã hóa rất nhiều json. Chỉ có một nhóm toàn cầu nên json khác nhau. Người chia sẻ chia sẻ nó. Lý do không thể thực hiện được cho giao diện json.Marshal là vì các byte được trả về cho người dùng và người dùng không có cách nào để "trả lại" các byte cho nhóm. Vì vậy, nếu bạn đang thực hiện nhiều mã hóa, json.Marshal luôn có một bộ đệm khá ít.
Aktau

@Flimzy: bạn có chắc không? Mã nguồn vẫn cho biết nó đọc toàn bộ giá trị vào bộ đệm trước khi giải mã: github.com/golang/go/blob/master/src/encoding/json/ Lỗi . Các Bufferedphương pháp là có để cho phép bạn xem bất kỳ dữ liệu bổ sung mà được đọc vào bộ đệm bên trong sau khi giá trị.
James Henstridge

@JamesHenstridge: Không, bạn có thể đúng. Tôi chỉ diễn giải câu nói của bạn khác với dự định của bạn. Xin lỗi vì sự nhầm lẫn.
Flimzy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.