Metody JSON, toJSON

Ř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ána JSON.stringify .

No