Kodowanie znaków JSON - czy UTF-8 jest dobrze obsługiwany przez przeglądarki, czy powinienem używać numerycznych sekwencji ucieczki?

89

Piszę usługę sieciową, która używa json do reprezentowania swoich zasobów i trochę utknąłem, myśląc o najlepszym sposobie zakodowania json. Czytając json rfc ( http://www.ietf.org/rfc/rfc4627.txt ) widać, że preferowanym kodowaniem jest utf-8. Ale rfc opisuje również mechanizm ucieczki ciągów do określania znaków. Zakładam, że byłoby to generalnie używane do ucieczki przed znakami spoza ASCII, dzięki czemu wynikowe utf-8 byłoby prawidłowe.

Powiedzmy, że mam ciąg json zawierający znaki Unicode (punkty kodowe), które nie są ASCII. Czy moja usługa sieciowa powinna po prostu zakodować to za pomocą utf-8 i zwrócić to, czy też powinna uciec przed wszystkimi znakami spoza ASCII i zwrócić czyste ASCII?

Chciałbym, aby przeglądarki mogły wykonywać wyniki przy użyciu jsonp lub eval. Czy to wpływa na decyzję? Brakuje mojej wiedzy na temat obsługi języka JavaScript w różnych przeglądarkach dla utf-8.

EDYCJA: Chciałem wyjaśnić, że moim głównym problemem związanym z kodowaniem wyników jest tak naprawdę obsługa wyników przez przeglądarkę. Z tego, co przeczytałem, wynika, że ​​przeglądarki mogą być wrażliwe na kodowanie, szczególnie podczas korzystania z JSONP. Nie znalazłem żadnych naprawdę dobrych informacji na ten temat, więc muszę zacząć robić testy, aby zobaczyć, co się stanie. Idealnie chciałbym uciec tylko od tych kilku znaków, które są wymagane i tylko kodować wyniki utf-8.

schickb
źródło

Odpowiedzi:

88

Specyfikacja JSON wymaga obsługi UTF-8 przez dekodery. W rezultacie wszystkie dekodery JSON mogą obsługiwać kod UTF-8 tak samo dobrze, jak mogą obsługiwać numeryczne sekwencje specjalne. Dotyczy to również interpreterów JavaScript, co oznacza, że ​​JSONP będzie obsługiwać również JSON zakodowany w UTF-8.

Możliwość używania przez kodery JSON numerycznych sekwencji ucieczki zamiast tego oferuje po prostu większy wybór. Jednym z powodów, dla których możesz wybrać numeryczne sekwencje ucieczki, byłoby to, że mechanizm transportu między koderem a zamierzonym dekoderem nie jest bezpieczny binarnie.

Inną przyczyną może chcesz użyć numeryczne sekwencje jest zapobieganie niektórych znaków występujących w strumieniu, takie jak <, &i ", które mogą być interpretowane jako HTML sekwencje jeśli kod JSON jest umieszczona bez ucieczki w HTML lub przeglądarka błędnie interpretuje ją jako HTML . Może to być obrona przed wstrzyknięciem HTML lub skryptami między witrynami (uwaga: niektóre znaki MUSZĄ zostać zmienione w JSON, w tym "i\ ).

Niektóre frameworki, w tym implementacja JSON w PHP, zawsze wykonują numeryczne sekwencje specjalne po stronie kodera dla dowolnego znaku spoza ASCII. Ma to na celu zapewnienie maksymalnej kompatybilności z ograniczonymi mechanizmami transportu i tym podobnymi. Nie należy tego jednak interpretować jako wskazania, że ​​dekodery JSON mają problem z UTF-8.

Więc myślę, że możesz po prostu zdecydować, którego użyć w następujący sposób:

  • Po prostu użyj UTF-8, chyba że twoja metoda przechowywania lub transportu między koderem a dekoderem nie jest bezpieczna binarnie.

  • W przeciwnym razie użyj numerycznych sekwencji sterujących.

thomasrutter
źródło
1
„Wszystkie dekodery JSON obsługują UTF-8” Chociaż dotyczy to przeglądarek, tylko dlatego, że wymaga tego standard, nie oznacza, że ​​całe oprogramowanie dekodujące JSON obsługuje UTF-8.
Michael Mior
7
„Wszystkie dekodery JSON obsługują UTF-8” jest dosłownie prawdą. Jeśli coś nie akceptuje UTF-8, nie jest to dekoder JSON. Może być podobny do dekodera JSON, ale na pewno nim nie jest.
thomasrutter
Myślę, że to zależy od
używanej przez
Powodem, dla którego RFC 8259 określa obsługę UTF-8 jako obowiązkową, jest to, że to właśnie świat ustandaryzował. Poprzednie przestarzałe specyfikacje definiowały ciągi jako Unicode, ale nie określały, które kodowanie; implementacje i tak ustandaryzowane na UTF-8 i zaktualizowana specyfikacja to odzwierciedla.
thomasrutter
O ile wiem, obsługa UTF-8 nie jest określona jako obowiązkowa w tym dokumencie RFC dla żadnego konkretnego oprogramowania. Jedyną wzmianką o UTF-8 jest to, że musi być używany jako kodowanie dla JSON wymienianych poza systemem zamkniętym. Nie oznacza to, że wszystkie dekodery JSON (język nie używany w RFC) muszą obsługiwać UTF-8.
Michael Mior
17

Miałem tam problem. Kiedy koduję ciąg znaków w formacie JSON ze znakiem takim jak „é”, wszystkie przeglądarki zwracają to samo „é”, z wyjątkiem IE, który zwróci „\ u00e9”.

Następnie z PHP json_decode (), nie powiedzie się, jeśli znajdzie "é", więc dla Firefox, Opera, Safari i Chrome, muszę wywołać utf8_encode () przed json_decode ().

Uwaga: w moich testach IE i Firefox używają swojego natywnego obiektu JSON, inne przeglądarki używają json2.js.

Tim Tisdall
źródło
10
Prawdopodobnie oznaczało utf8_encode(), php.net/manual/en/function.utf8-encode.php
Beniamin
4
Jeśli IE nie potrafi tego zdekodować, oznacza to błąd w dowolnym dekoderze JSON, którego używasz. Wszystkie dekodery JSON muszą pomyślnie zdekodować zakodowaną formę lub nie są dekoderem JSON. Jeśli chodzi o problem z json_decode () z é unescaped, możliwe, że przesyłany tekst nie jest UTF-8. Dekodery JSON zawsze zakładają UTF-8, nawet implementację PHP, mimo że PHP normalnie nie zakłada UTF-8 w wielu innych funkcjach. Istnieją inne kodowania znaków, które mogą zawierać é bez zmiany znaczenia i wyglądać identycznie na ekranie, ale nie są to UTF-8. Obejściem tego problemu jest kodowanie w formie \ uXXXX.
thomasrutter
Mówiąc tylko: JSON może legalnie występować w dowolnym kodowaniu Unicode (UTF-8, UTF-16 BE / LE, UTF32 BE / LE, z lub bez znacznika kolejności bajtów). A ponieważ ASCII jest podzbiorem UTF-8, może również występować w ASCII. Nie wiem, czy parsery akceptują na przykład UTF-32.
gnasher729
1
To prawda, a parsery nie muszą obsługiwać niczego innego niż UTF-8. Ze specyfikacji: „Tekst JSON MUSI być zakodowany w UTF-8, UTF-16 lub UTF-32. Domyślne kodowanie to UTF-8, a teksty JSON zakodowane w UTF-8 są interoperacyjne w tym sensie, że będą być pomyślnie odczytane przez maksymalną liczbę implementacji; istnieje wiele implementacji, które nie mogą z powodzeniem odczytać tekstów w innych kodowaniach (takich jak UTF-16 i UTF-32). Implementacje NIE MOGĄ dodawać znaku kolejności bajtów na początku tekstu JSON. "
thomasrutter
@thomasrutter Podana specyfikacja jest stara. Aktualna specyfikacja mówi: " tekst JSON wymieniane między systemami, które nie są częścią zamkniętego ekosystemu muszą być zakodowane przy użyciu UTF-8 Poprzedni specyfikacje JSON nie wymagały użycia UTF-8 przy przekazywaniu tekstu JSON Jednak zdecydowana większość.. implementacji oprogramowania opartych na formacie JSON zdecydowało się na użycie kodowania UTF-8, w zakresie, w jakim jest to jedyne kodowanie zapewniające interoperacyjność. Implementacje NIE MOGĄ dodawać znaku kolejności bajtów (U + FEFF) na początku przesyłanego w sieci Tekst JSON.
Remy Lebeau
12

ASCII już w nim nie ma. Używanie kodowania UTF-8 oznacza, że ​​nie używasz kodowania ASCII. Mechanizm ucieczki powinien służyć do tego, co mówi RFC:

Wszystkie znaki Unicode można umieścić w cudzysłowie, z wyjątkiem znaków, które muszą być zmienione: cudzysłów, odwrócone solidus i znaki sterujące (od U ​​+ 0000 do U + 001F)

chaos
źródło
1
Jeśli przeczytasz ten cytat, który podałeś, zobaczysz, że nie musisz uciekać przed wszystkimi znakami Unicode, tylko kilka znaków specjalnych. Ale musisz zakodować wyniki (najlepiej za pomocą utf-8). Tak więc pytanie brzmi: „Po co zawracać sobie głowę ucieczką przed zwykłymi znakami Unicode, jeśli używasz kodowania utf-8”.
schickb
Ponadto łańcuch zakodowany w ascii jest czystym podzbiorem utf-8. Jeśli użyję funkcji ucieczki json dla wszystkich znaków spoza ASCII, wynikiem będzie ascii - a zatem utf-8. Różne biblioteki json (takie jak python simplejson) mają tryby wymuszania wyników ascii. Przypuszczam z jakiegoś powodu, na przykład wykonanie w przeglądarkach.
schickb
Kiedy zawracasz sobie głowę ucieczką od normalnych znaków Unicode, znajdujesz się w kontekstach, w których są metaznakami, takimi jak ciągi. (Fragment RFC, który zacytowałem, dotyczy łańcuchów; przepraszam, nie było to jasne.) Nie musisz cały czas robić wyjścia ASCII; Myślę, że to więcej do debugowania w zepsutych przeglądarkach.
chaos
7

Miałem ten sam problem. Mi to pasuje. Proszę to sprawdzić.

json_encode($array,JSON_UNESCAPED_UNICODE);
Ankit Sewadik
źródło
Należy zauważyć, że powyższe jest PHP, ponieważ pytanie w żaden sposób nie jest specyficzne dla PHP i dotyczy tylko usługi internetowej, która również może nie używać PHP (jak starsi nasi czytelnicy mogą nadal pamiętać…)
ntninja
1

Czytając json rfc ( http://www.ietf.org/rfc/rfc4627.txt ) widać, że preferowanym kodowaniem jest utf-8.

FYI, RFC 4627 nie jest już oficjalną specyfikacją JSON. Został przestarzały w 2014 r. Przez RFC 7159 , który następnie został przestarzały w 2017 r. Przez RFC 8259 , który jest obecną specyfikacją.

RFC 8259 stwierdza:

8.1. Kodowanie znaków

Tekst JSON wymieniany między systemami, które nie są częścią zamkniętego ekosystemu MUSI być zakodowany przy użyciu UTF-8 [RFC3629] .

Poprzednie specyfikacje formatu JSON nie wymagały użycia UTF-8 podczas przesyłania tekstu JSON. Jednak zdecydowana większość implementacji oprogramowania opartych na JSON zdecydowała się na użycie kodowania UTF-8, do tego stopnia, że ​​jest to jedyne kodowanie, które zapewnia interoperacyjność.

Implementacje NIE MOGĄ dodawać znaku kolejności bajtów (U + FEFF) na początku tekstu JSON przesyłanego przez sieć. Ze względu na interoperacyjność, implementacje analizujące teksty JSON MOGĄ ignorować obecność znaku kolejności bajtów, zamiast traktować ją jako błąd.

Remy Lebeau
źródło
0

Miałem podobny problem z é char ... Myślę, że komentarz „możliwe, że tekst, który podajesz, nie jest UTF-8” jest prawdopodobnie bliski znaku. Mam wrażenie, że domyślne sortowanie w mojej instancji było czymś innym, dopóki nie zdałem sobie sprawy i nie zmieniłem na utf8 ... Problem polega na tym, że dane już tam były, więc nie jestem pewien, czy przekonwertował dane, czy nie, kiedy je zmieniłem, wyświetla się dobrze w mysql stoł warsztatowy. Wynik końcowy jest taki, że php nie zakoduje danych w formacie json, po prostu zwróci false. Nie ma znaczenia, jakiej przeglądarki używasz jako serwera powodującego mój problem, php nie przeanalizuje danych do utf8, jeśli ten znak jest obecny. Jak mówię, nie jestem pewien, czy jest to spowodowane konwersją schematu do utf8 po danych, czy po prostu błędem php. W tym przypadku użyjjson_encode(utf8_encode($string));

Paul Smith
źródło