[Урок 11] . Формат JSON, как хранилище любых данных в iMacros.

Ранее, мы рассмотрели формат CSV. Для получения информации из любой ячейки при помощи iMacros и Javascript там используется конструкция, вроде такой — tableData[1][2].  С одной стороны это удобно, но в больших таблицах с кучей столбцов можно легко запутаться под каким индексом находятся необходимые данные. Это проблему решает другой формат и называется он — JSON.

Структура формата JSON

JSON(JavaScript Object Notation) —  простой формат обмена данными, удобный для чтения и написания как человеком, так и компьютером. Он используется для представления объектов в виде строки и это самый удобный формат данных для взаимодействия с Javascript. В браузерах есть множество замечательных методов, знание тонкостей которых делает операции с JSON простыми и удобными.

Данные в формате JSON могут состоять из:

  • JavaScript-объекты { ... } или
  • Массивы [ ... ] или
  • Значения одного из типов:
    • строки в двойных кавычках,
    • число,
    • логическое значение true/false,
    • null.

Давайте возьмем структуру данных из прошлого урока и сделаем из нее JS-объект, который потом можно будет преобразовать в валидный JSON:

Старая структура для CSV:

var arr = [
	[1,"John Smith",1961],
	[2,"Mike Trump",1975],
	[3,"Oliver Stone",1982]
];

Новая структура для JSON:

var obj = {
	title: "Citizens",
	peoples: [
		{
			order: 1,
			name: "John Smith",
			year: 1961
		},
		{
			order: 2,
			name: "Mike Trump",
			year: 1975
		},
		{
			order: 3,
			name: "Oliver Stone",
			year: 1982
		}
	]
};

Может показаться, что кода стало больше и становится не совсем ясно, к чему такие преобразования. На самом деле, у такого массива может быть бесконечная вложенность с полноценной иерархией, что в табличной структуре невозможно. К тому же, мы можем обращаться к элементам такого массива по именному ключу, например:

obj.title              // "Citizens"
obj.peoples[0].name    // "John Smith"
obj.peoples[1]["year"] // 1975

Это действительно очень удобно и мы можем создавать по-настоящему гибкие массивы и совершать над ними любые манипуляции.
В Javascript два основных метода для работы с JSON — это преобразование строки в Javascript Object и обратное преобразование объекта в строку. 

Метод JSON.parse() — превращаем строку в JS-объект

Чаще всего этот метод используется, когда мы с сервера делаем запрос и получаем обычную строку с текстом в формате JSON. Мы не можем сразу обращаться к ключам и значениям объектов в этой строке. Javascript интерпретатор пока что воспринимает эту строку, как строку и не знает, что на самом деле, там содержатся разные объекты. Поэтому, необходимо сначала преобразовать эту строку в Javascript-объект, чтобы дальше можно было работать с ней, как с объектом.

Для этого и существует метод JSON.parse().

Примеры:

// Массив чисел
var nums = '[555, 666, 777]';
window.console.log(nums[0]); // "[" - первый символ строки

nums = JSON.parse(nums); // преобразование в обычный массив
window.console.log(nums[0]); // 555 - первый элемент массива

// Наш прошлый объект
var obj = '{"title": "Citizens","peoples": [{"order": 1,"name": "John Smith","year": 1961},{"order": 2,"name": "Mike Trump","year": 1975},{"order": 3,"name": "Oliver Stone","year": 1982}]}';
window.console.log(obj.title); //undefined

obj = JSON.parse(obj); // преобразование в обычный объект
window.console.log(obj.title); // Citizens

Примечание: Обратите внимание, ключи(например «title») в объекте я обернул двойными кавычками. Для ключей одинарные кавычки являются ошибкой. Еще в формате JSON не поддерживаются комментарии, для их поддержки есть специальный формат JSON5.

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

Пример со вторым аргументом JSON.parse(text, replacer):

// Дата в строке - в формате UTC
var str = '{"title":"My Birthday","date":"1952-01-20T12:00:00.000Z"}';

var calendar = JSON.parse(str);
// "getDate()" - это встроенный метод объекта "new Date()"
window.console.log( calendar.date.getDate() ); // Ошибка undefined 

var calendar = JSON.parse(str, function(key, value) {
	if (key == 'date') {return new Date(value);}
	return value;
});

window.console.log( calendar.date.getDate() ); // 20 - день месяца

Учтите, что эта функция рекурсивная и она проходит абсолютно по каждому значению вашего объекта, независимо от вложенности. Лично я, редко использую данную возможность, но иногда она бывает очень к месту.

Метод JSON.stringify() — превращаем JS-объект в строку

Как вы уже догадались, это функция для обратного преобразования объекта в строку. Учтите, что если в вашем объекте попадется функция — она не будет преобразована и высока вероятность возникновения ошибки.

Пример простого преобразования:

var str = {
	title: "My Birthday",
	date: new Date("1952-01-20")
};

str = JSON.stringify(str);

window.console.log( str ); //  {"title":"My Birthday","date":"1952-01-20T00:00:00.000Z"}

Обратите внимание, наш объект Date превратился в строку в формате UTC, это произошло потому, что у него есть встроенный метод toJSON. Проверьте сами в FireBug при помощи автокомплита.

Исключение свойств, функция replacer

В качестве второго параметра вы можете указать список свойств, которые нужно преобразовать в строку.

Например:

var str = {
	title: "My Birthday",
	date: new Date("1952-01-20"),
	window: window,
	age: 15
};

window.console.log( JSON.stringify(str, ["title", "date"]) );
// {"title":"My Birthday","date":"1952-01-20T00:00:00.000Z"}}

Тут я добавил в наш объект 2 новых свойства, которые были проигнорированы при преобразовании в строку, поскольку не были добавлены, как разрешенные.

Также метод JSON.stringify() поддерживает в качестве параметра функцию-реплейсер и работает аналогично методу JSON.parse().

Пример с аргументом функции:

var str = {
	title: "My Birthday",
	date: new Date("1952-01-20"),
	window: window,
	age: 15
};

var str = JSON.stringify(str, function(key, value) {
	if (key == 'window' || key == 'age') return undefined;
	return value;
});

window.console.log( str );
// {"title":"My Birthday","date":"1952-01-20T00:00:00.000Z"}

Добавляем красоты и отступов при форматировании

Помимо первых двух параметров, у метода JSON.stringify() есть и третий, который отвечает за количество отступов перед каждым ключом. Измеряется отступ в количестве пробелов.

var str = {
	title: "My Birthday",
	date: new Date("1952-01-20")
};

window.console.log( JSON.stringify(str) );
// {"title":"My Birthday","date":"1952-01-20T00:00:00.000Z"}

window.console.log( JSON.stringify(str, "", 4) );
/*
{
    "title": "My Birthday",
    "date": "1952-01-20T00:00:00.000Z"
}
 */

Создаем свой метод для чтения и записи JSON

В очередной раз мы воспользуемся функциями для чтения и записи файлов из прошлых уроков, но добавим еще и преобразование объекта в JSON и наоборот. Еще, будет уместно добавить обработку ошибок, например, если вы открываете некорректный JSON. Подобные ошибки могу привести к остановке работы iMacros.

Сохранение объекта в JSON

// Функция для сохранения массива в файл в формате JSON
var saveToJSON = function (fileName, obj) {

	try {

		var fileDescriptor = imns.FIO.openNode(fileName);
		imns.FIO.writeTextFile( fileDescriptor, JSON.stringify(obj, null, 4) );		

	} catch (e) {		
		return e;
	}
	return true;
 
};

// Тестовый объект
var obj = {
	title: "My Birthday",
	date: new Date("1952-01-20"),
	window: window
};
 
// Сохраняем в формате JSON
var cb = saveToJSON("C:\\BOTS\\iMacros\\test.json", obj);
// Проверяем корректно ли сохранилось
if (cb == true) {
	window.console.log('Сохранено успешно!');
} else {
	window.console.log('Ошибка сохранения');
	window.console.log(cb);
}

Чтение из JSON и преобразование в объект Javascript

// Функция для сохранения массива в файл в формате JSON
var readFromJSON = function (fileName) {

	try {

		var fileDescriptor = imns.FIO.openNode(fileName);
		var text = imns.FIO.readTextFile(fileDescriptor);	
		var obj = JSON.parse(text);
		return obj;

	} catch (e) {		
		
		return false;
	}
 
};
 
// Читаем из формата JSON
var cb = readFromJSON("C:\\BOTS\\iMacros\\test.json");

// Если открылось правильно - увидим распарсенный объект, иначе - false
window.console.log(cb); 

Таким нехитрым образом можно сохранять и считывать огромные массивы данных.

Если все наши небольшие, но удобные функции собрать в один большой объект, может получиться отличнейшая библиотека для работы с iMacros+Javascript, которая сэкономит кучу времени на различных рутинных операциях и добавит потрясающие возможности вашим ботам. Вы сможете подключать такую библиотеку в начало каждого вашего скрипта и использовать весь функционал, который мы рассмотрели за все это время. 

Я потихоньку уже начал работать над такой библиотекой и в скором времени она перерастет в отдельный проект со своей подробной документацией и коммьюнити.

11 комментариев

  1. Спасибо, весьма познавательно!
    Хочу еще спросить, возможно ли сделать, что бы json файл обновлялся при каждом запуске скрипта?
    То есть, при первом запуске создается структура [] и добавляется объект. При всех последующих запусках скрипта, просто дописываются объекты с разделителем ‘,’ через appendTextFile например

    Ответить
    • Ден, конечно можно!

      Только appendTextFile тут не подойдет, поскольку нам нужно дописывать не совсем в конец файла.
      Вам необходимо считать файл в формате JSON, преобразовать его в javascript-объект, при помощи метода push вы можете добавить новый объект в массив, после чего преобразуете массив в строку и сохраняете в файл.

      P.S. не забудьте добавить в код функции saveToJSON и readFromJSON из урока, чтобы код ниже заработал.

      Пример кода:

      var obj = {
      	title: "Citizens",
      	peoples: [
      		{
      			order: 1,
      			name: "John Smith",
      			year: 1961
      		}
      	]
      };
      
      // Сохраняем в JSON
      saveToJSON("C:\\BOTS\\iMacros\\test.json", obj);
      
      // Считываем с диска JSON, получаем JS-объект
      var cb = readFromJSON("C:\\BOTS\\iMacros\\test.json");
      
      // Добавляем в массив еще один объект
      cb.push({
      	order: 2,
      	name: "Mike Trump",
      	year: 1975
      });
      
      // Сохраняем в JSON массив уже с двумя объектами
      saveToJSON("C:\\BOTS\\iMacros\\test.json", cb);
      
      Ответить
      • Решил эту задачу через создание массива и запись туда старого и нового объекта через .concat и последующую перезапись в json файл. Но ваш вариант на порядок лучше. Спасибо!

        Ответить
  2. Давно читаю ваши статьи, спасибо за качественный контент. Также хотел спросить почему вы не объявляете функцию напрямую, а даете значение переменной. Так ведь нужно писать переменные в начале скрипта, что в принципе не критично, но ведь объявление функции сразу же ставит её в глобальную область видимости и в будущем, при расширении функционала, никогда не возникнет проблем. Это никак не высер в вашу сторону, просто интересно.

    Ответить
  3. Скажите пожалуйста, если пришел ответ вида:

    {«status»:»SUCCESS»,»link»:»http://vk.com/wall-12345_67890″}

    как из него вырезать или извлечь ссылку и перейти по ней?

    Ответить
  4. Не корректно отобразилось предыдущее сообщение, вот такого вида ответ:
    {«status»:»SUCCESS»,»link»:»http://vk.com/wall-12345_67890″}

    Ответить
  5. «Ошибка сохранения» jsplayer.js:200:1
    TypeError: cyclic object value
    Стек-трейс:
    saveToJSON@chrome://imacros/content/jsplayer.js:178:43
    @chrome://imacros/content/jsplayer.js:195:10
    JS_Player.prototype.play@chrome://imacros/content/jsplayer.js:172:13
    obj.playJSFile@chrome://imacros/content/iMacros.js:961:13
    obj.play@chrome://imacros/content/iMacros.js:642:13
    oncommand@chrome://imacros/content/iMacrosSidebar.xul:1:1

    Ответить

Оставить комментарий