Tworzę klienta API, w którym muszę zakodować ładunek JSON na żądanie i zdekodować treść JSON na podstawie odpowiedzi.
Przeczytałem kod źródłowy z kilku bibliotek i z tego, co widziałem, mam zasadniczo dwie możliwości kodowania i dekodowania łańcucha JSON.
Użyj json.Unmarshal
przekazywania całego ciągu odpowiedzi
data, err := ioutil.ReadAll(resp.Body)
if err == nil && data != nil {
err = json.Unmarshal(data, value)
}
lub za pomocą json.NewDecoder.Decode
err = json.NewDecoder(resp.Body).Decode(value)
W moim przypadku, gdy mamy do czynienia z odpowiedziami HTTP, które się implementują io.Reader
, wydaje się, że druga wersja wymaga mniej kodu, ale skoro widziałam obie, zastanawiam się, czy jest jakaś preferencja, czy powinienem użyć rozwiązania, czy innej.
Co więcej, przyjęta odpowiedź na to pytanie mówi
Użyj
json.Decoder
zamiastjson.Unmarshal
.
ale nie podał przyczyny. Czy naprawdę powinienem unikać używania json.Unmarshal
?
ioutil.ReadAll
jest prawie zawsze zły pomysł. Nie jest to związane z twoim celem, ale wymaga posiadania wystarczającej ilości ciągłej pamięci, aby przechowywać wszystko, co może zejść z potoku, nawet jeśli ostatnie 20 TB odpowiedzi przypada po ostatnim}
w twoim JSON.io.LimitReader
aby temu zapobiec.Odpowiedzi:
To zależy od twojego wkładu. Jeśli spojrzysz na implementację
Decode
metodyjson.Decoder
, buforuje ona całą wartość JSON w pamięci przed jej rozłączeniem na wartość Go. Więc w większości przypadków nie będzie już wydajniejszej pamięci (chociaż może to łatwo zmienić w przyszłej wersji języka).Lepsza ogólna zasada jest następująca:
json.Decoder
jeśli dane pochodzą zeio.Reader
strumienia lub musisz zdekodować wiele wartości ze strumienia danych.json.Unmarshal
jeśli masz już dane JSON w pamięci.W przypadku czytania z żądania HTTP wybrałbym,
json.Decoder
ponieważ oczywiście czytasz ze strumienia.źródło
Buffered
metoda umożliwia wyświetlanie dodatkowych danych, które zostały odczytane do bufora wewnętrznego po wartości.