Programiści

Czym jest JSON? Składnia, typy danych i częste błędy

7 min czytania

JSON (JavaScript Object Notation) to tekstowy format zapisu danych w postaci par klucz-wartość i uporządkowanych list, zbudowany zaledwie z sześciu typów wartości. To format, w którym niemal każde API wysyła i odbiera dane, spotykany też w plikach konfiguracyjnych, wpisach logów i bazach NoSQL. Mimo że wygląda jak literał obiektu JavaScript, JSON ma niewielki, ale ściśle egzekwowany zestaw reguł, a złamanie choćby jednej z nich sprawia, że cały dokument staje się nie do sparsowania.

Czym naprawdę jest JSON

Każdy dokument JSON składa się dokładnie z sześciu typów wartości:

  • String, wyłącznie w podwójnym cudzysłowie, jak "witaj"
  • Liczba, jak 42 albo 3.14
  • Wartość logiczna, true albo false
  • null
  • Obiekt, nieuporządkowany zbiór par klucz-wartość ujęty w {}
  • Tablica, uporządkowana lista wartości ujęta w []

To cały system typów. Nie ma typu daty, nie ma undefined, nie ma typu funkcyjnego, nic więcej. Obiekty i tablice mogą się zagnieżdżać dowolnie głęboko, ale każda wartość na każdym poziomie musi być jednym z tych sześciu typów.

Reguły składniowe towarzyszące tym typom są równie sztywne:

  • Klucze obiektu muszą być stringami w podwójnym cudzysłowie. {"name":"Ada"} jest poprawny; {name:"Ada"} już nie.
  • Żadnych przecinków na końcu. Ostatnia właściwość obiektu i ostatni element tablicy nie mogą mieć przecinka po sobie.
  • Żadnych komentarzy, ani //, ani /* */.
  • Stringi i klucze zawsze w podwójnym cudzysłowie. Pojedynczy cudzysłów nie jest poprawną składnią JSON, mimo że jest poprawny w JavaScripcie.
  • Klucza nigdy nie da się zostawić bez cudzysłowu, nawet jeśli nazwa jest banalnie prosta.
  • NaN, Infinity i undefined nie są poprawnymi wartościami JSON, mimo że wszystkie trzy są poprawne w JavaScripcie.
  • Funkcji nie da się zapisać w JSON. Nie ma sposobu na serializację zachowania, tylko danych.

Złamanie którejkolwiek z tych reguł powoduje, że parser JSON zatrzymuje się i zgłasza błąd, zamiast zgadywać, o co chodziło.

JSON kontra literał obiektu JavaScript

Składnia {} i [] w JSON została zapożyczona z literałów obiektów i tablic JavaScript, i właśnie dlatego te dwie rzeczy tak łatwo się mylą. Na pierwszy rzut oka wyglądają niemal identycznie, ale literał obiektu JavaScript jest bardziej pobłażliwy w każdym punkcie, w którym JSON jest rygorystyczny.

CechaJSONLiterał obiektu JavaScript
Klucze muszą być w cudzysłowieTak, tylko podwójny cudzysłówNie, cudzysłów opcjonalny
Przecinek na końcu dozwolonyNieTak
Komentarze dozwoloneNieTak
Stringi w pojedynczym cudzysłowieNieTak
Funkcje jako wartościNieTak
undefined jako wartośćNieTak

Tu właśnie ginie mnóstwo czasu podczas debugowania. Skopiowanie literału obiektu wprost z pliku JavaScript, z niecytowanymi kluczami i przecinkiem na końcu, w miejsce, które oczekuje JSON, daje kod działający bez zarzutu jako JavaScript, ale wysypujący się w chwili, gdy dotknie go JSON.parse. JSON nie jest podzbiorem składni JavaScript, który można traktować swobodnie, to osobny, bardziej rygorystyczny format, który po prostu dzieli z JavaScriptem część interpunkcji.

Przykład praktyczny: minifikacja i formatowanie prawdziwej odpowiedzi API

Weźmy typowy obiekt użytkownika, jaki mogłoby zwrócić API:

{
  "id": 1,
  "name": "Ada Lovelace",
  "email": "[email protected]",
  "roles": [
    "admin",
    "user"
  ],
  "active": true,
  "meta": null
}

Ta wersja sformatowana, z wcięciem 2 spacji, liczy 145 znaków. Te same dane zminifikowane, ze wszystkimi spacjami i znakami nowej linii usuniętymi, wyglądają tak:

{"id":1,"name":"Ada Lovelace","email":"[email protected]","roles":["admin","user"],"active":true,"meta":null}

To 107 znaków, około 35% mniej, a różnica wynika wyłącznie z białych znaków. Ani jedna wartość, ani jeden klucz się nie zmienił, dane są identyczne.

Właśnie dlatego API wysyłają JSON zminifikowany przez sieć: przy dużej skali odcięcie tych 35% bajtów w milionach żądań przekłada się na realne oszczędności przepustowości i czasu odpowiedzi, a żaden kod po stronie klienta nie potrzebuje wcięć, żeby poprawnie sparsować wartość. Ten sam zminifikowany ciąg jest jednak niemal nieczytelny dla człowieka. Kiedy debugujesz odpowiedź i musisz zobaczyć zagnieżdżenie, nazwy kluczy i to, gdzie zaczyna się i kończy tablica, formatujesz go z powrotem do wersji z wcięciami. Te same dane, dwa różne zastosowania: jedno dla maszyn, drugie dla ciebie.

Sformatuj swój własny JSON

Wklej poniżej zminifikowany albo niechlujnie sformatowany JSON, żeby zobaczyć go z porządnymi wcięciami, albo zrób odwrotnie i zminifikuj sformatowany dokument, zanim wyślesz go tam, gdzie liczy się rozmiar.

Formater i Walidator JSON
Za darmo, bez rejestracji, na każdym urządzeniu.
Otwórz pełne narzędzie

Częste błędy i przypadki brzegowe

Poniższe komunikaty pochodzą z V8, silnika JavaScript stojącego za Chrome i Node.js. To właśnie ten silnik uruchamia JSON.parse po stronie klienta w przeglądarce większości odwiedzających. Dokładne brzmienie potrafi się nieznacznie różnić między przeglądarkami (SpiderMonkey w Firefoksie formułuje błędy inaczej), ale parsowanie zawsze zawodzi w ten sam sposób.

Przecinek na końcu. Dla wejścia {"id":1,"name":"Ada",} V8 zgłasza:

Expected double-quoted property name in JSON at position 21 (line 1 column 22)

Przecinek po "Ada" nie ma po sobie niczego, a JSON nie ma pojęcia opcjonalnego separatora na końcu.

Niecytowany klucz. {id:1} kończy się błędem:

Expected property name or '}' in JSON at position 1 (line 1 column 2)

Parser oczekuje stringa w podwójnym cudzysłowie zaraz po {, a znajduje goły identyfikator.

Klucz w pojedynczym cudzysłowie. {'a':1} trafia w tę samą klasę błędu:

Expected property name or '}' in JSON at position 1 (line 1 column 2)

Pojedynczy cudzysłów nie jest poprawnym sposobem otwarcia stringa w JSON, więc parser traktuje 'a' dokładnie tak, jak potraktowałby niecytowany klucz: to po prostu nie jest poprawna nazwa właściwości.

NaN i undefined jako wartości. {"a":NaN} daje:

Unexpected token 'N', "{"a":NaN}" is not valid JSON

A {"a":undefined} daje:

Unexpected token 'u', "{"a":undefined}" is not valid JSON

Obie wartości są legalne w literale obiektu JavaScript, ale nie mają żadnej reprezentacji w JSON. Jeśli w twojej aplikacji jakaś wartość może być NaN albo undefined, musisz zamienić ją na null albo string, zanim ją zserializujesz.

Zduplikowane klucze cicho się nadpisują. {"a":1,"a":2} jest składniowo poprawnym JSON, bez żadnego błędu, i parsuje się do {"a":2}. Większość parserów, w tym parser JavaScript, zachowuje tylko ostatnie wystąpienie powtórzonego klucza i po cichu odrzuca wcześniejsze. To subtelne źródło realnych błędów: narzędzie budujące, które łączy fragmenty konfiguracji, albo ręcznie edytowany plik, w którym ktoś wkleił ten sam blok dwa razy, może skończyć z tym samym kluczem powtórzonym dwukrotnie. Skoro nie ma żadnego błędu, nikt tego nie zauważa, dopóki gdzieś dalej nie wyskoczy zła wartość.

Najczęściej zadawane pytania

Czym jest JSON? JSON (JavaScript Object Notation) to tekstowy format danych strukturalnych, zbudowany z sześciu typów wartości: stringów, liczb, wartości logicznych, null, obiektów i tablic. To najpopularniejszy format żądań i odpowiedzi API, używany też w plikach konfiguracyjnych i wielu bazach danych.

Czy JSON to to samo co obiekt JavaScript? Nie. JSON wygląda jak literał obiektu JavaScript, ale jest bardziej rygorystyczny: klucze muszą być w podwójnym cudzysłowie, przecinki na końcu są niedozwolone, komentarze są niedozwolone, a wartości takie jak undefined czy funkcja nie mają w nim żadnej reprezentacji. Literał obiektu JavaScript pozwala na wszystko z tej listy, JSON nie pozwala na nic.

Dlaczego dodatkowy przecinek psuje mój JSON? Ponieważ gramatyka JSON nie przewiduje żadnej reguły dla dodatkowego przecinka po ostatnim elemencie obiektu czy tablicy. Parser czytający {"id":1,"name":"Ada",} oczekuje po przecinku kolejnej nazwy właściwości, a zamiast tego napotyka zamykający nawias klamrowy, co dokładnie opisuje błąd “Expected double-quoted property name in JSON”.

Czy JSON obsługuje komentarze? Nie. W specyfikacji JSON nie ma żadnej składni dla komentarzy // czy /* */. Jeśli musisz opisać plik JSON dla ludzi, typowymi obejściami są osobny plik dokumentacji, klucz sąsiedni w rodzaju "_comment" traktowany jak zwykłe dane, albo zmiana formatu na taki, który komentarze wspiera, na przykład JSON5 albo YAML.

Jaka jest różnica między formatowaniem a minifikacją JSON? Formatowanie (nazywane też upiększaniem czy prettyfikacją) dodaje wcięcia i podziały linii, żeby człowiek mógł od razu ogarnąć strukturę wzrokiem. Minifikacja usuwa wszystkie te białe znaki i produkuje najkrótszy możliwy string. Obie wersje reprezentują dokładnie te same dane, różnica jest czysto prezentacyjna, i konwersja między nimi w obie strony jest normalną praktyką w zależności od tego, czy JSON czyta człowiek, czy przesyła go sieć.

Czy moje dane są wysyłane na serwer podczas korzystania z formatera online? W przypadku Formatera JSON od pluri.tools, nie. Formatowanie, minifikacja i walidacja działają wyłącznie na JSON.parse i JSON.stringify w silniku JavaScript twojej przeglądarki. Nie ma tu żadnego fetcha ani połączenia sieciowego, więc JSON, który wklejasz, nigdy nie opuszcza twojego urządzenia.

Formater i Walidator JSON
Teraz wypróbuj samodzielnie w pełnym narzędziu.
Wypróbuj teraz