Řekněme, že máme složitý objekt a chtěli bychom ho převést na řetězec, odeslat po síti nebo jen vytisknout pro účely protokolování.
Takový řetězec by přirozeně měl obsahovat všechny důležité vlastnosti.
Převod bychom mohli implementovat takto:
let user = {
name: "John",
age: 30,
toString() {
return `{name: "${this.name}", age: ${this.age}}`;
}
};
alert(user); // {name: "John", age: 30}
…Ale v procesu vývoje se přidávají nové vlastnosti, staré vlastnosti se přejmenovávají a odebírají. Aktualizace takového toString
pokaždé se může stát bolestí. Mohli bychom v něm zkusit procházet vlastnosti, ale co když je objekt složitý a má vnořené objekty ve vlastnostech? Potřebovali bychom také implementovat jejich konverzi.
Naštěstí není potřeba psát kód, aby to všechno zvládl. Úloha již byla vyřešena.
JSON.stringify
JSON (JavaScript Object Notation) je obecný formát pro reprezentaci hodnot a objektů. Je popsána jako ve standardu RFC 4627. Původně byl vytvořen pro JavaScript, ale mnoho dalších jazyků má knihovny, které to také zvládají. Je tedy snadné používat JSON pro výměnu dat, když klient používá JavaScript a server je napsán na Ruby/PHP/Java/Whatever.
JavaScript poskytuje metody:
JSON.stringify
převést objekty do formátu JSON.JSON.parse
převést JSON zpět na objekt.
Například zde máme JSON.stringify
student:
let student = {
name: 'John',
age: 30,
isAdmin: false,
courses: ['html', 'css', 'js'],
spouse: null
};
let json = JSON.stringify(student);
alert(typeof json); // we've got a string!
alert(json);
/* JSON-encoded object:
{
"name": "John",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"spouse": null
}
*/
Metoda JSON.stringify(student)
vezme objekt a převede ho na řetězec.
Výsledný json
řetězec se nazývá kódovaný JSON nebo serializované nebo stringifikovaný nebo řazeno objekt. Jsme připraveni je odeslat po drátě nebo uložit do úložiště obyčejných dat.
Upozorňujeme, že objekt kódovaný JSON má několik důležitých rozdílů od literálu objektu:
- Řetězce používají dvojité uvozovky. Žádné jednoduché uvozovky nebo zadní zaškrtnutí v JSON. Takže
'John'
se změní na"John"
. - Názvy vlastností objektů jsou také v uvozovkách. To je povinné. Takže
age:30
se změní na"age":30
.
JSON.stringify
lze použít i na primitiva.
JSON podporuje následující datové typy:
- Objekty
{ ... }
- Pole
[ ... ]
- Primitiva:
- řetězce,
- čísla,
- logické hodnoty
true/false
, null
.
Například:
// a number in JSON is just a number
alert( JSON.stringify(1) ) // 1
// a string in JSON is still a string, but double-quoted
alert( JSON.stringify('test') ) // "test"
alert( JSON.stringify(true) ); // true
alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]
JSON je specifikace na jazyku nezávislá pouze na datech, takže některé vlastnosti objektů specifické pro JavaScript jsou přeskočeny pomocí JSON.stringify
.
Jmenovitě:
- Vlastnosti funkce (metody).
- Symbolické klíče a hodnoty.
- Vlastnosti, které ukládají
undefined
.
let user = {
sayHi() { // ignored
alert("Hello");
},
[Symbol("id")]: 123, // ignored
something: undefined // ignored
};
alert( JSON.stringify(user) ); // {} (empty object)
Obvykle je to v pořádku. Pokud to není to, co chceme, brzy uvidíme, jak proces přizpůsobit.
Skvělé je, že vnořené objekty jsou podporovány a automaticky převedeny.
Například:
let meetup = {
title: "Conference",
room: {
number: 23,
participants: ["john", "ann"]
}
};
alert( JSON.stringify(meetup) );
/* The whole structure is stringified:
{
"title":"Conference",
"room":{"number":23,"participants":["john","ann"]},
}
*/
Důležité omezení:nesmí existovat žádné kruhové odkazy.
Například:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: ["john", "ann"]
};
meetup.place = room; // meetup references room
room.occupiedBy = meetup; // room references meetup
JSON.stringify(meetup); // Error: Converting circular structure to JSON
Zde převod selže z důvodu cyklického odkazu:room.occupiedBy
odkazy meetup
a meetup.place
odkazy room
:
Vyloučení a transformace:náhradník
Úplná syntaxe JSON.stringify
je:
let json = JSON.stringify(value[, replacer, space])
- hodnota
- Hodnota ke kódování.
- náhradník
- Pole vlastností ke kódování nebo mapovací funkce
function(key, value)
. - mezera
- Množství místa pro formátování
Většinou JSON.stringify
se používá pouze s prvním argumentem. Pokud však potřebujeme doladit proces výměny, například odfiltrovat cyklické odkazy, můžeme použít druhý argument JSON.stringify
.
Pokud mu předáme pole vlastností, budou zakódovány pouze tyto vlastnosti.
Například:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]}
Tady jsme asi příliš přísní. Seznam vlastností se aplikuje na celou strukturu objektu. Tedy objekty v participants
jsou prázdné, protože name
není v seznamu.
Zahrňte do seznamu všechny vlastnosti kromě room.occupiedBy
což by způsobilo kruhový odkaz:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']) );
/*
{
"title":"Conference",
"participants":[{"name":"John"},{"name":"Alice"}],
"place":{"number":23}
}
*/
Nyní vše kromě occupiedBy
je serializován. Ale seznam vlastností je poměrně dlouhý.
Naštěstí můžeme místo pole použít funkci jako replacer
.
Funkce bude volána pro každých (key, value)
pár a měl by vrátit „nahrazenou“ hodnotu, která bude použita místo původní. Nebo undefined
pokud má být hodnota přeskočena.
V našem případě můžeme vrátit value
„jak je“ pro vše kromě occupiedBy
. Chcete-li ignorovat occupiedBy
, níže uvedený kód vrátí undefined
:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, function replacer(key, value) {
alert(`${key}: ${value}`);
return (key == 'occupiedBy') ? undefined : value;
}));
/* key:value pairs that come to replacer:
: [object Object]
title: Conference
participants: [object Object],[object Object]
0: [object Object]
name: John
1: [object Object]
name: Alice
place: [object Object]
number: 23
occupiedBy: [object Object]
*/
Vezměte prosím na vědomí, že replacer
funkce získá každý pár klíč/hodnota včetně vnořených objektů a položek pole. Aplikuje se rekurzivně. Hodnota this
uvnitř replacer
je objekt, který obsahuje aktuální vlastnost.
První hovor je speciální. Vyrábí se pomocí speciálního „obalového objektu“:{"": meetup}
. Jinými slovy, první (key, value)
pár má prázdný klíč a hodnota je cílový objekt jako celek. Proto je první řádek ":[object Object]"
ve výše uvedeném příkladu.
Cílem je poskytnout co nejvíce energie pro replacer
jak je to možné:má možnost v případě potřeby analyzovat a nahradit/přeskočit i celý objekt.
Formátování:mezera
Třetí argument z JSON.stringify(value, replacer, space)
je počet mezer, které se mají použít pro pěkné formátování.
Dříve všechny stringified objekty neměly žádné odsazení a mezery navíc. To je v pořádku, pokud chceme poslat objekt přes síť. space
argument se používá výhradně pro pěkný výstup.
Zde space = 2
říká JavaScriptu, aby zobrazoval vnořené objekty na více řádcích s odsazením 2 mezer uvnitř objektu:
let user = {
name: "John",
age: 25,
roles: {
isAdmin: false,
isEditor: true
}
};
alert(JSON.stringify(user, null, 2));
/* two-space indents:
{
"name": "John",
"age": 25,
"roles": {
"isAdmin": false,
"isEditor": true
}
}
*/
/* for JSON.stringify(user, null, 4) the result would be more indented:
{
"name": "John",
"age": 25,
"roles": {
"isAdmin": false,
"isEditor": true
}
}
*/
Třetí argument může být také řetězec. V tomto případě se pro odsazení namísto počtu mezer použije řetězec.
space
Parametr se používá výhradně pro účely protokolování a nice-output.
Vlastní „toJSON“
Jako toString
pro převod řetězce může objekt poskytovat metodu toJSON
pro převod do JSON. JSON.stringify
automaticky jej zavolá, je-li k dispozici.
Například:
let room = {
number: 23
};
let meetup = {
title: "Conference",
date: new Date(Date.UTC(2017, 0, 1)),
room
};
alert( JSON.stringify(meetup) );
/*
{
"title":"Conference",
"date":"2017-01-01T00:00:00.000Z", // (1)
"room": {"number":23} // (2)
}
*/
Zde vidíme, že date
(1)
se stal strunou. Je to proto, že všechna data mají vestavěný toJSON
metoda, která vrací takový druh řetězce.
Nyní přidáme vlastní toJSON
pro náš objekt room
(2)
:
let room = {
number: 23,
toJSON() {
return this.number;
}
};
let meetup = {
title: "Conference",
room
};
alert( JSON.stringify(room) ); // 23
alert( JSON.stringify(meetup) );
/*
{
"title":"Conference",
"room": 23
}
*/
Jak vidíme, toJSON
se používá jak pro přímé volání JSON.stringify(room)
a když room
je vnořen do jiného zakódovaného objektu.
JSON.parse
K dekódování řetězce JSON potřebujeme jinou metodu s názvem JSON.parse.
Syntaxe:
let value = JSON.parse(str, [reviver]);
- str
- Řetězec JSON k analýze.
- oživovač
- Volitelná funkce (klíč,hodnota), která bude volána pro každý
(key, value)
pár a může transformovat hodnotu.
Například:
// stringified array
let numbers = "[0, 1, 2, 3]";
numbers = JSON.parse(numbers);
alert( numbers[1] ); // 1
Nebo pro vnořené objekty:
let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
let user = JSON.parse(userData);
alert( user.friends[1] ); // 1
JSON může být tak složitý, jak je potřeba, objekty a pole mohou obsahovat jiné objekty a pole. Musí se však řídit stejným formátem JSON.
Zde jsou typické chyby v ručně psaném JSON (někdy jej musíme napsat pro účely ladění):
let json = `{
name: "John", // mistake: property name without quotes
"surname": 'Smith', // mistake: single quotes in value (must be double)
'isAdmin': false // mistake: single quotes in key (must be double)
"birthday": new Date(2000, 2, 3), // mistake: no "new" is allowed, only bare values
"friends": [0,1,2,3] // here all fine
}`;
Kromě toho JSON nepodporuje komentáře. Přidáním komentáře do JSON se stane neplatným.
Existuje další formát s názvem JSON5, který umožňuje klíče bez uvozovek, komentáře atd. Toto je však samostatná knihovna, která není ve specifikaci jazyka.
Běžný JSON je tak přísný ne proto, že by jeho vývojáři byli líní, ale proto, aby umožnil snadnou, spolehlivou a velmi rychlou implementaci algoritmu analýzy.
Použití reviver
Představte si, že máme řetězec meetup
objekt ze serveru.
Vypadá to takto:
// title: (meetup title), date: (meetup date)
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
…A teď musíme deserializovat pro přepnutí zpět na objekt JavaScript.
Udělejme to zavoláním JSON.parse
:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str);
alert( meetup.date.getDate() ); // Error!
Jejda! Chyba!
Hodnota meetup.date
je řetězec, nikoli Date
objekt. Jak by mohl JSON.parse
vědět, že by měl tento řetězec transformovat na Date
?
Pojďme na JSON.parse
oživovací funkce jako druhý argument, který vrací všechny hodnoty „tak jak jsou“, ale date
se změní na Date
:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str, function(key, value) {
if (key == 'date') return new Date(value);
return value;
});
alert( meetup.date.getDate() ); // now works!
Mimochodem, to funguje i pro vnořené objekty:
let schedule = `{
"meetups": [
{"title":"Conference","date":"2017-11-30T12:00:00.000Z"},
{"title":"Birthday","date":"2017-04-18T12:00:00.000Z"}
]
}`;
schedule = JSON.parse(schedule, function(key, value) {
if (key == 'date') return new Date(value);
return value;
});
alert( schedule.meetups[1].date.getDate() ); // works!
Shrnutí
- JSON je datový formát, který má svůj vlastní nezávislý standard a knihovny pro většinu programovacích jazyků.
- JSON podporuje prosté objekty, pole, řetězce, čísla, logické hodnoty a
null
. - JavaScript poskytuje metody JSON.stringify pro serializaci do JSON a JSON.parse pro čtení z JSON.
- Obě metody podporují funkce transformátoru pro inteligentní čtení/zápis.
- Pokud má objekt
toJSON
, pak je volánaJSON.stringify
.