Як розібрати JSON за допомогою Node.js?


971

Як я повинен аналізувати JSON за допомогою Node.js? Чи є якийсь модуль, який буде надійно перевіряти та аналізувати JSON?

Відповіді:


1099

Ви можете просто використовувати JSON.parse.

Визначення JSONоб'єкта є частиною специфікації ECMAScript 5 . node.js побудований на двигуні V8 Google Chrome , який відповідає стандарту ECMA. Тому node.js також має глобальний об’єкт [docs] .JSON

Примітка - JSON.parseможе зв’язати поточну нитку, оскільки це синхронний метод. Тож якщо ви плануєте розбирати великі об’єкти JSON, використовуйте потоковий аналізатор json.


Хтось знає, чому цього немає в офіційній документації? Або, якщо він є, де його знайти?
snapfractalpop

34
@snapfractalpop: Документація описує лише функції тощо, які є частиною node.js. Стандартні функції JavaScript є частиною V8 , на якій побудовано node.js. Відповідно я оновив відповідь.
Фелікс Клінг

1
@FelixKling Для чого це варто, тут є багато речей на вікі github вузла: github.com/joyent/node/wiki/…
damianb

тут я опублікував демонстраційну версію, де ви можете бачити та грати з цією відповіддю в Інтернеті (приклад розбору - у файлі app.js - потім натисніть кнопку запуску та побачите результат у терміналі): посилання ви можете змінити код і побачити вплив ...
nathan g

Ваша відповідь вимагає попереднього знання синтаксису JavaScript. Наскільки важко було б показати приклад використання? JSON.parse (str); // це noob-friendly і тому краща відповідь
webb

661

Ви можете вимагати .json файлів.

var parsedJSON = require('./file-name');

Наприклад, якщо у вас є config.jsonфайл у тому самому каталозі, що і ваш файл вихідного коду, ви використовуєте:

var config = require('./config.json');

або (розширення файлу можна опустити):

var config = require('./config');

зауважте, що requireце синхронно і читає файл лише один раз , після викликів повертає результат із кешу

Також зверніть увагу: Ви повинні використовувати це лише для локальних файлів під вашим абсолютним контролем, оскільки це потенційно може виконувати будь-який код у файлі.


4
Якщо ви використовуєте цей метод для розбору файлу, обов'язково врахуйте шлях до необхідності. Наприклад, вам може знадобитися зробити щось подібне: вимагати "./file-name-with-no-extension" (наприклад, якщо файл знаходиться в поточному каталозі)
SnapShot

94
Зауважте, що відповідь є кешованою. Наприклад, якщо ви ставите вище, вимагаєте викликати функцію, зателефонувати в функцію, змінити файл JSON і зателефонувати знову, ви отримаєте стару версію файлу JSON. Зловив мене пару разів!
Бен Клейтон

15
Зауважте також, що requireце синхронно. Якщо ви хочете асинхронізувати дружнє використання fs.readFileзамість цьогоJSON.parse
Еван Моран

29
Чи буде цей підхід просто трактувати файл як JavaScript, таким чином потенційно працює довільний код у файлі .json?
d11wtq

15
Проста примітка: не забудьте скористатися .jsonрозширенням! Якщо у вашому файлі НЕ є .jsonрозширення, потрібно не розглядати його як json-файл.
Джейсон

323

Можна використовуватиJSON.parse() .

Ви повинні мати можливість використовувати JSONоб'єкт у будь-якій сумісній програмі JavaScript ECMAScript 5 . І V8 , на якому побудований Node.js, є одним із них.

Примітка. Якщо ви використовуєте файл JSON для зберігання конфіденційної інформації (наприклад, паролів), це неправильний спосіб зробити це. Подивіться, як це робить Heroku: https://devcenter.heroku.com/articles/config-vars#setting-up-config-vars-for-a-deployed-application . Дізнайтеся, як це робить ваша платформа, і скористайтеся process.envдля отримання конфігураційних параметрів з коду.


Розбір рядка, що містить дані JSON

var str = '{ "name": "John Doe", "age": 42 }';
var obj = JSON.parse(str);

Розбір файлу, що містить дані JSON

Вам доведеться виконати деякі файлові операції з fsмодулем.

Асинхронна версія

var fs = require('fs');

fs.readFile('/path/to/file.json', 'utf8', function (err, data) {
    if (err) throw err; // we'll not consider error handling for now
    var obj = JSON.parse(data);
});

Синхронна версія

var fs = require('fs');
var json = JSON.parse(fs.readFileSync('/path/to/file.json', 'utf8'));

Ви хочете використовувати require? Подумати ще раз!

Ви можете іноді використовуватиrequire :

var obj = require('path/to/file.json');

Але я не рекомендую цього з кількох причин:

  1. requireє синхронним. Якщо у вас дуже великий файл JSON, він задушить ваш цикл подій. Вам дійсно потрібно використовувати JSON.parseз fs.readFile.
  2. requireпрочитає файл лише один раз . Подальші дзвінки до requireтого ж файлу повернуть кешовану копію. Непогана ідея, якщо ви хочете читати .jsonфайл, який постійно оновлюється. Ви можете використовувати хак . Але в цей момент простіше простого використання fs.
  3. Якщо ваш файл не має .jsonрозширення, requireвін не сприйме вміст файлу як JSON.

Серйозно! ВикористовуйтеJSON.parse .


load-json-file модуль

Якщо ви читаєте велику кількість .jsonфайлів (і якщо ви дуже ліниві), вам стає прикро кожен раз писати кодовий код. За допомогою load-json-fileмодуля можна зберегти деякі символи .

const loadJsonFile = require('load-json-file');

Асинхронна версія

loadJsonFile('/path/to/file.json').then(json => {
    // `json` contains the parsed object
});

Синхронна версія

let obj = loadJsonFile.sync('/path/to/file.json');

Розбір JSON від потоків

Якщо вміст JSON передається через мережу, вам потрібно використовувати потоковий аналізатор JSON. В іншому випадку він зв’яже ваш процесор і задушить цикл подій, поки вміст JSON не буде повністю потоково передано.

Для цього в NPM є багато пакетів . Виберіть те, що найкраще для вас.


Поводження з помилками / безпека

Якщо ви не впевнені, то , що передається JSON.parse()є дійсним JSON , переконайтеся , що вкласти виклик JSON.parse()всередині try/catchблоку. Користувач, який надав рядок JSON, може призвести до збоїв у вашій програмі та навіть може призвести до ям у безпеці. Переконайтеся, що обробка помилок виконується, якщо ви аналізуєте наданий зовнішньою програмою JSON.


2
and could even lead to security holesз цікавості, як?
natario

6
@natario: Тут мова йде про сервер JS. Припустимо, хтось розбирає наданий користувачем JSON. Якщо припущення полягає в тому, що JSON завжди добре сформований, зловмисник може надіслати кілька неправильно сформованих JSON, щоб викликати помилку, яка, якщо перекинеться на сторону клієнта, може виявити життєво важливу інформацію про систему. Або якщо JSON був неправильно сформований і містив текст <script>..., а помилка перекинулася на сторону клієнта, у вас є помилка XSS. Тому IMO важливо обробляти помилки JSON саме там, де ви їх розбираєте.
sampathsris

1
@NickSteele: Однак я змінив "це не рекомендується" на "не рекомендую". Я сподіваюся, що ви зараз щасливі.
sampathsris

1
@NickSteele: Враховуючи вказані вами недоліки, я не думаю, що це добре розроблена функція. Мені здається, що деякі люди думали, "ей, чи не було б круто використовувати requireдля включення JSON?" і навіть не турбував документування побічних ефектів. Це також означало, що потрібно приймати файли двома мовами: JavaScript та JSON (ні, вони не однакові). Стільки для СРП.
sampathsris

1
@NickSteele: Так, лише для конфігурації він працює добре. Але JSON використовується не лише для конфігурації.
sampathsris

85

використовувати об'єкт JSON :

JSON.parse(str);

12
Це просто дублює верхню відповідь. Будь ласка, розглянути питання про її видалення; ти збережеш очки.
Дан Даскалеску

6
Ця відповідь надіслала 50 відгуків. Згідно з правилом 1% , ймовірно, 5000 користувачів витратили час на читання цієї відповіді, що нічого не додає до першої. Той факт, що йому 3 роки, лише погіршує проблему :)
Дан Даскалеску

16
@DanDascalescu - Якщо ви помітите, дві відповіді були опубліковані точно в той самий час 3 роки тому. Вони обидва надають однакову інформацію. Це так у всьому ЗО, я не збираюся виписувати половину своїх відповідей лише тому, що вони не були прийнятою відповіддю.
Марк Кан

8
Я, наприклад, вважав цю серію коментарів досить цікавою, але сама відповідь - це марний час. ... Я не впевнений, якщо це означає, що відповідь слід видалити, бо тоді я б не бачив нитку коментарів. Але в іншому випадку я б сказав так.
MalcolmOcean

7
@DanDascalescu, я вважаю, що ця відповідь є більш зрозумілою і зрозумілою. Прийнятий не дає приклад використання та заплутаний через багато посилань та додаткових матеріалів.
andresgottlieb

37

Ще один приклад JSON.parse:

var fs = require('fs');
var file = __dirname + '/config.json';

fs.readFile(file, 'utf8', function (err, data) {
  if (err) {
    console.log('Error: ' + err);
    return;
  }

  data = JSON.parse(data);

  console.dir(data);
});

2
Мені подобається, що цей підхід не вимагає, щоб файл json був локальним для програми. Дякую!
Чарльз Брандт

35

Я хотів би зазначити, що існують альтернативи глобальному об'єкту JSON. JSON.parseі JSON.stringifyвони обидва синхронні, тому якщо ви хочете мати справу з великими об'єктами, ви можете перевірити деякі з асинхронних модулів JSON.

Подивіться: https://github.com/joyent/node/wiki/Modules#wiki-parsers-json


1
Це особливо актуально, якщо очікується, що дані JSON від вхідних з'єднань. Якщо неправильний JSON буде проаналізований JSON.parseвсією вашою програмою, вийде з ладу або, використовуючи process.on('uncaughtException', function(err) { ... });, з часом не буде шансу надіслати користувачеві помилку "неправильний JSON".
Павло

3
Який з них asyncпарсер? Я не знайшов.
bxshi

3
Пов’язана сторінка тепер позначена як "ПОСТАНОВЕНО" та описує себе як "зів'ялу реліквію".
ніхто

30

Включіть node-fsбібліотеку.

var fs = require("fs");
var file = JSON.parse(fs.readFileSync("./PATH/data.json", "utf8"));

Для отримання додаткової інформації про бібліотеку 'fs', зверніться до документації за адресою http://nodejs.org/api/fs.html


2
Можливо, варто відзначити, що слід обернути рядок файлів var у спробу / ловити на випадок, якщо ваш JSON не зможе розібратися або файл не існує.
Фоста

3
Або просто використовувати зворотний дзвінок!
lawx

10

Оскільки ви не знаєте, що ваша строка дійсно дійсна, я б поставив її спочатку в спробу лову. Крім того, оскільки блоки спробу лову не оптимізовані вузлом, я б поставив всю річ в іншу функцію:

function tryParseJson(str) {
    try {
        return JSON.parse(str);
    } catch (ex) {
        return null;
    }
}

АБО в "асинхронному стилі"

function tryParseJson(str, callback) {
    process.nextTick(function () {
      try {
          callback(null, JSON.parse(str));
      } catch (ex) {
          callback(ex)
      }
    })
}

2
Я просто хочу зауважити, що process.nextTick не є aysnc. Це просто відкладання читання файлу до наступного виклику функції в циклі подій JS. Щоб запустити JSON.parse асинхронно, ви використовуєте інший потік, ніж основний потік Node.js
Alexander Mills


7

Усі тут розповіли про JSON.parse, тому я подумав сказати щось інше. Є чудовий модуль Підключення до багатьох проміжних програм, щоб полегшити та покращити розробку програм. Одне з проміжних програм - bodyParser . Він аналізує JSON, html-форми та ін. Існує також специфічне проміжне програмне забезпечення для розбору JSON тільки noop .

Подивіться на посилання вище, це може бути вам дуже корисно.



6

як згадуються тут інші відповіді, ви, мабуть, хочете або вимагати локальний файл json, який, на вашу думку, є безпечним і присутнім, як файл конфігурації:

var objectFromRequire = require('path/to/my/config.json'); 

або використовувати глобальний об'єкт JSON для розбору значення рядка в об'єкт:

var stringContainingJson = '\"json that is obtained from somewhere\"';
var objectFromParse = JSON.parse(stringContainingJson);

зауважте, що коли вам потрібен файл, оцінюється вміст цього файлу, що вводить ризик для безпеки, якщо це не файл json, а js-файл.

тут я опублікував демонстраційну версію, де ви можете побачити обидва методи та грати з ними в Інтернеті (приклад розбору - у файлі app.js - потім натисніть кнопку запуску та побачите результат у терміналі): http: // staging1 .codefresh.io / labs / api / env / json-parse-example

ви можете змінити код і побачити вплив ...


5

Використовуєте JSON для конфігурації з Node.js? Прочитайте це і отримайте свої навички налаштування понад 9000 ...

Примітка. Люди, які стверджують, що дані = вимагають ('./ data.json'); є ризиком для безпеки і прихильнюючи відповіді людей із ревним завзяттям: Ви абсолютно і зовсім помиляєтесь . Спробуйте помістити в цей файл не-JSON ... Вузол дасть вам помилку, точно так само, як якщо б ви зробили те ж саме з набагато повільнішим і складнішим кодом для читання файлів вручну, а потім подальшого JSON.parse (). Будь ласка, припиніть поширювати дезінформацію; ти шкодиш світу, не допомагаєш. Вузол був розроблений, щоб це допустити; це не ризик для безпеки!

Власні програми поставляються в 3+ шарах конфігурації:

  1. Конфігурація сервера / контейнера
  2. Конфігурація програми
  3. (необов'язково) Конфігуратор орендаря / спільноти / організації
  4. Конфігурація користувача

Більшість розробників трактують їх сервер та конфігурацію додатків так, ніби вони можуть змінитися. Це не може. Ви можете наносити зміни на більш високі шари один на одного, але ви змінюєте базові вимоги . Деякі речі повинні існувати! Зробіть так, щоб ваш конфігурація діяв так, як це непорушно, оскільки частина його в основному є, як і ваш вихідний код.

Якщо не помітити, що багато ваших речей не буде змінено після запуску, це призводить до анти-моделей, таких як засмічення завантаження конфігурації блоками спробу / лову, і вигляд, що ви можете продовжувати без належної програми налаштування. Ви не можете. Якщо ви можете, це належить до конфігураційного рівня спільноти / користувача, а не конфігураційного рівня сервера / програми. Ти просто робиш це неправильно. Факультативний матеріал повинен бути шаруватий зверху, коли додаток закінчується завантаженням.

Перестаньте стукати головою об стіну: ваш конфігурація повинна бути надто простою .

Погляньте на те, як легко налаштувати щось настільки складне, як протокол-агностик та сервіс даних-джерела агностіки, використовуючи простий файл конфігурації json та простий файл app.js ...

container-config.js ...

{
    "service": {
        "type"  : "http",
        "name"  : "login",
        "port"  : 8085
    },
    "data": {
        "type"  : "mysql",
        "host"  : "localhost",
        "user"  : "notRoot",
        "pass"  : "oober1337",
        "name"  : "connect"
    }
}

index.js ... (двигун, який працює на все)

var config      = require('./container-config.json');       // Get our service configuration.
var data        = require(config.data.type);            // Load our data source plugin ('npm install mysql' for mysql).
var service     = require(config.service.type);         // Load our service plugin ('http' is built-in to node).
var processor   = require('./app.js');                  // Load our processor (the code you write).

var connection  = data.createConnection({ host: config.data.host, user: config.data.user, password: config.data.pass, database: config.data.name });
var server      = service.createServer(processor);
connection.connect();
server.listen(config.service.port, function() { console.log("%s service listening on port %s", config.service.type, config.service.port); });

app.js ... (код, який надає повноваження вашій протокольно-агностичній службі та агностичній службі джерела даних)

module.exports = function(request, response){
    response.end('Responding to: ' + request.url);
}

За допомогою цього шаблону ви можете зараз завантажувати файли спільноти та налаштування користувачів поверх завантаженого додатка, програма розробників готова засунути вашу роботу в контейнер і масштабувати її. Ви читали для багатостороннього. Користувальниця ізольована. Тепер ви можете відокремити питання про те, який протокол обслуговування ви використовуєте, тип бази даних, який ви використовуєте, і просто зосередитися на написанні хорошого коду.

Оскільки ви використовуєте шари, ви можете покладатися на єдине джерело істини для всіх, в будь-який час (шарувата об'єкта конфігурації), і перевірка помилок уникнути на кожному кроці, піклуючись про «про лайно, як я збираюся зробити це працювати без належної конфігурації?!? ".


4

Моє рішення:

var fs = require('fs');
var file = __dirname + '/config.json';

fs.readFile(file, 'utf8', function (err, data) {
    if (err) {
        console.log('Error: ' + err);
        return;
    }

    data = JSON.parse(data);

    console.dir(data);
});

Дякую @eloyesp, я спробував використовувати цей код, але я постійно отримую TypeError: path must be a string or Bufferпомилки - будь-яка ідея, з чого почати налагодження цієї проблеми?
GPP

4

Просто хочу виконати відповідь (коли я боровся з нею деякий час), хочу показати, як отримати доступ до інформації json, у цьому прикладі показано доступ до Json Array:

var request = require('request');
request('https://server/run?oper=get_groups_joined_by_user_id&user_id=5111298845048832', function (error, response, body) {
  if (!error && response.statusCode == 200) {
    var jsonArr = JSON.parse(body);
    console.log(jsonArr);
    console.log("group id:" + jsonArr[0].id);
  }
})


3

Просто, щоб зробити це якомога складніше і принести якомога більше пакетів ...

const fs = require('fs');
const bluebird = require('bluebird');
const _ = require('lodash');
const readTextFile = _.partial(bluebird.promisify(fs.readFile), _, {encoding:'utf8',flag:'r'});
const readJsonFile = filename => readTextFile(filename).then(JSON.parse);

Це дозволяє:

var dataPromise = readJsonFile("foo.json");
dataPromise.then(console.log);

Або якщо ви використовуєте async / wait:

let data = await readJsonFile("foo.json");

Перевага перед просто використанням readFileSyncполягає в тому, що ваш сервер Node може обробляти інші запити під час зчитування файлу з диска.


2

JSON.parse не забезпечить безпеку рядка json, який ви розбираєте. Ви повинні дивитися на бібліотеку на зразок json-safe-розбору або подібної бібліотеки.

З сторінки json-safe-розбору npm:

JSON.parse чудовий, але він має один серйозний недолік у контексті JavaScript: він дозволяє переосмислити успадковані властивості. Це може стати проблемою, якщо ви аналізуєте JSON з ненадійного джерела (наприклад, користувача) та викликуте на ньому функцій, які ви могли б очікувати існування.


2

Використовуйте спробу функції Лодаша повернути об'єкт помилки, з яким можна обробити функцію isError.

// Returns an error object on failure
function parseJSON(jsonString) {
   return _.attempt(JSON.parse.bind(null, jsonString));
}


// Example Usage
var goodJson = '{"id":123}';
var badJson = '{id:123}';
var goodResult = parseJSON(goodJson);
var badResult = parseJSON(badJson);

if (_.isError(goodResult)) {
   console.log('goodResult: handle error');
} else {
   console.log('goodResult: continue processing');
}
// > goodResult: continue processing

if (_.isError(badResult)) {
   console.log('badResult: handle error');
} else {
   console.log('badResult: continue processing');
}
// > badResult: handle error

3
Чи можете ви пояснити, чому ви додали .bindзамість того, щоб просто використовувати _.attempt (JSON.parse, str)
steviejay

2

Завжди обов'язково використовуйте JSON.parse у блоці спробу лову, оскільки вузол завжди кидає несподівану помилку, якщо у json у вас є пошкоджені дані, тому використовуйте цей код замість простого JSON.Parse

try{
     JSON.parse(data)
}
catch(e){
   throw new Error("data is corrupted")
  }

1

Якщо ви хочете додати деякі коментарі до свого JSON та дозволити пропуски комок, можливо, ви хочете використовувати нижче виконання:

var fs = require('fs');

var data = parseJsData('./message.json');

console.log('[INFO] data:', data);

function parseJsData(filename) {
    var json = fs.readFileSync(filename, 'utf8')
        .replace(/\s*\/\/.+/g, '')
        .replace(/,(\s*\})/g, '}')
    ;
    return JSON.parse(json);
}

Зверніть увагу, що це може не спрацювати, якщо у вас є щось подібне "abc": "foo // bar"у вашому JSON. Так YMMV.


1

Якщо вихідний файл JSON досить великий, можливо, потрібно розглянути асинхронний маршрут через натиснутий підхід async / очікувати з Node.js 8.0 наступним чином

const fs = require('fs')

const fsReadFile = (fileName) => {
    fileName = `${__dirname}/${fileName}`
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, 'utf8', (error, data) => {
            if (!error && data) {
                resolve(data)
            } else {
                reject(error);
            }
        });
    })
}

async function parseJSON(fileName) {
    try {
        return JSON.parse(await fsReadFile(fileName));
    } catch (err) {
        return { Error: `Something has gone wrong: ${err}` };
    }
}

parseJSON('veryBigFile.json')
    .then(res => console.log(res))
    .catch(err => console.log(err))

1

Я використовую fs-extra . Мені це дуже подобається, тому що, хоча він підтримує зворотні дзвінки, він також підтримує Обіцяння . Тож це просто дозволяє мені записати свій код набагато читабельнішим чином:

const fs = require('fs-extra');
fs.readJson("path/to/foo.json").then(obj => {
    //Do dome stuff with obj
})
.catch(err => {
    console.error(err);
});

Він також має багато корисних методів, які не поєднуються зі стандартним fsмодулем, і , крім того, він також з'єднує методи з рідного fsмодуля та обіцяє їх.

ПРИМІТКА. Ви все ще можете використовувати вбудовані методи Node.js. Вони обіцяні та скопійовані у fs-extra. Переглянути примітки до fs.read()&fs.write()

Так що це в основному всі переваги. Я сподіваюся, що інші вважають це корисним.


1

Якщо вам потрібно захистити JSON з Node.js захищеним способом (він же: користувач може вводити дані або публічний API), я б запропонував використовувати secure-json-синтаксичний аналіз .

Використання, як за замовчуванням, JSON.parseале захистить ваш код від:

const badJson = '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "constructor": {"prototype": {"bar": "baz"} } }'

const infected = JSON.parse(badJson)
console.log(infected.x) // print undefined

const x = Object.assign({}, infected)
console.log(x.x) // print 7

const sjson = require('secure-json-parse')
console.log(sjson.parse(badJson)) // it will throw by default, you can ignore malicious data also

0

Ви можете використовувати JSON.parse () (яка є вбудованою функцією, яка, ймовірно, змусить вас обернути це заявами про випробування).

Або використовуйте яку-небудь бібліотеку JSON для розбору npm, щось на зразок json-parse-або



0

NodeJs - сервер на базі JavaScript , тому ви можете робити так, як це робите в чистому JavaScript ...

Уявіть, у вас цей Json у NodeJs ...

var details = '{ "name": "Alireza Dezfoolian", "netWorth": "$0" }';
var obj = JSON.parse(details);

І ви можете зробити вище, щоб отримати проаналізовану версію вашого json ...


0

Як згадувалося у вищенаведених відповідях, ми можемо використовувати JSON.parse()для розбору рядків JSON. Але перед тим, як проаналізувати, не забудьте проаналізувати правильні дані, інакше це може звести всю вашу програму

безпечно використовувати його так

let parsedObj = {}
try {
    parsedObj = JSON.parse(data);
} catch(e) {
    console.log("Cannot parse because data is not is proper json format")
}

0

Використовуйте JSON.parse(str);. Детальніше про це читайте тут .

Ось кілька прикладів:

var jsonStr = '{"result":true, "count":42}';

obj = JSON.parse(jsonStr);

console.log(obj.count);    // expected output: 42
console.log(obj.result);   // expected output: true

-1

Більше ніяких модулів не потрібно.
Просто використовуйте,
var parsedObj = JSON.parse(yourObj);
я не думаю, що з цього питання є проблеми безпеки


-2

Це просто, ви можете конвертувати JSON в рядок за допомогою JSON.stringify(json_obj), а конвертувати рядок в JSON за допомогою JSON.parse("your json string").


2
Ви подивилися верхню відповідь на це питання? Це 3 роки і дуже повно. Що ви сподівалися зробити свій внесок у тривіальну інформацію, яку ви тут пропонуєте?
Робі Корнеліссен

2
Тепер, тепер не будемо дотримуватися подвійного стандарту
danielmhanover
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.