Розшифровка JSON за допомогою json.Unmarshal vs json.NewDecoder.Decode


202

Я розробляю клієнт 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 замінив виклик Unmarshal на json.NewDecoder для "видалення буфера в декодуванні JSON".
Мет

Це просто залежить від того, який вклад вам зручніше використовувати. blog.golang.org/json-and-go наводить приклади використання обох методів.
rexposadas

15
ІМО, ioutil.ReadAllце майже завжди неправильно , що потрібно робити. Це не пов’язано з вашою метою, але вимагає, щоб у вас було достатньо суцільної пам’яті, щоб зберігати все, що може зійти з труби, навіть якщо останній відповідь 20 ТБ після останнього }у вашому JSON.
Дастін

@Dustin Ви можете використовувати io.LimitReaderдля запобігання цьому.
Inanc Gumus

Відповіді:


240

Це дійсно залежить від того, який ваш внесок. Якщо ви подивитесь на реалізацію 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 завжди є досить трохи буферного відбиття.
Актау

@Flimzy: ти впевнений? Вихідний код все ще говорить, що він читає все значення в буфері перед декодуванням: github.com/golang/go/blob/master/src/encoding/json/… . BufferedМетод є , щоб ви бачите якісь - які додаткові дані, прочитані у внутрішній буфер після значення.
James Henstridge

@JamesHenstridge: Ні, ви, мабуть, праві. Я просто тлумачив вашу заяву інакше, ніж ви задумали. Вибачення за плутанину.
Flimzy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.