Что такое Google Apps Script и где он применяется
Google Apps Script (GAS) — это облачная платформа для разработки скриптов, основанная на JavaScript, которая позволяет автоматизировать задачи и расширять функциональность приложений Google Workspace (Sheets, Docs, Forms, Drive, Gmail и др.). GAS выполняется на серверах Google, обеспечивая интеграцию между различными сервисами Google и внешними API.
Платформа используется для создания пользовательских меню, диалоговых окон, автоматизации документооборота, обработки данных в таблицах, создания веб-приложений и многого другого.
Зачем преобразовывать объекты в строки?
Преобразование объектов JavaScript в строки — частая задача в разработке скриптов. Основные причины:
Логирование: Запись состояния объекта или данных в логи (например, Stackdriver Logging / Google Cloud Logging) для отладки или мониторинга.
Хранение данных: Сохранение структурированных данных в текстовом формате, например, в ячейках Google Sheets, в текстовых файлах на Google Drive или в PropertiesService.
Передача данных: Отправка данных через HTTP-запросы (API), вебхуки или передача данных между различными функциями или скриптами, где требуется строковое представление.
Сериализация: Подготовка данных для сохранения или передачи в формате, который легко восстановить обратно в объект (чаще всего JSON).
Обзор типов объектов, с которыми работает Apps Script
В Google Apps Script вы работаете как со стандартными объектами JavaScript (массивы, даты, пользовательские объекты), так и со специфичными объектами, предоставляемыми сервисами Google Workspace:
Стандартные объекты JavaScript: { key: 'value' }, ['item1', 'item2'], new Date().
Объекты сервисов GAS: SpreadsheetApp.getActiveSpreadsheet(), DocumentApp.getActiveDocument(), GmailApp.getInboxThreads(), CalendarApp.getEventsForDay(new Date()).
Объекты событий: Объекты, передаваемые в триггеры (например, e в функции onEdit(e)).
Понимание типа объекта важно для выбора корректного метода преобразования в строку.
Использование JSON.stringify() для преобразования объектов
Основным и наиболее распространенным методом преобразования объектов в строку в JavaScript и, следовательно, в Google Apps Script является JSON.stringify().
Синтаксис и параметры JSON.stringify()
Метод JSON.stringify() преобразует значение JavaScript в строку JSON.
/**
* Преобразует объект JavaScript в строку JSON.
*
* @param {any} value Значение для преобразования.
* @param {(key: string, value: any) => any | (string | number)[]} [replacer] Функция-заменитель или массив строк/чисел для фильтрации свойств.
* @param {string | number} [space] Строка или число для добавления отступов (форматирования) в вывод.
* @return {string} Строка JSON.
*/
JSON.stringify(value[, replacer[, space]])value: Преобразуемый объект.
replacer (необязательный): Функция для изменения процесса сериализации или массив строк/чисел, указывающий, какие свойства включать.
space (необязательный): Управляет отступами в выходной строке для удобочитаемости. Может быть числом (количество пробелов) или строкой (используется как отступ).
Примеры преобразования простых объектов в JSON-строку
/**
* Пример преобразования простого объекта конфигурации в JSON.
*/
function simpleObjectToString() {
const config = {
apiKey: 'XYZ123',
isEnabled: true,
retryCount: 3
};
// Преобразование в компактную JSON-строку
const jsonString = JSON.stringify(config);
Logger.log(jsonString); // Вывод: "{"apiKey":"XYZ123","isEnabled":true,"retryCount":3}"
// Преобразование в форматированную JSON-строку с отступом в 2 пробела
const formattedJsonString = JSON.stringify(config, null, 2);
Logger.log(formattedJsonString);
/* Вывод:
{
"apiKey": "XYZ123",
"isEnabled": true,
"retryCount": 3
}
*/
}Преобразование сложных объектов, включая массивы и вложенные объекты
JSON.stringify() легко справляется с массивами и вложенными структурами.
/**
* Пример преобразования сложного объекта с массивами и вложенностью.
*/
function complexObjectToString() {
const campaignData = {
id: 'CMP-001',
name: 'Summer Sale Campaign',
startDate: new Date(2023, 5, 1), // Июнь 1, 2023
targets: [
{ region: ' EMEA', budget: 5000 },
{ region: 'NA', budget: 10000 }
],
metadata: {
owner: 'marketing@example.com',
lastUpdated: new Date()
}
};
// Используем replacer для форматирования дат в ISO строку (хотя JSON.stringify делает это по умолчанию)
const replacer = (key, value) => {
if (value instanceof Date) {
return value.toISOString();
}
return value;
};
const jsonString = JSON.stringify(campaignData, replacer, 2);
Logger.log(jsonString);
/* Примерный вывод:
{
"id": "CMP-001",
"name": "Summer Sale Campaign",
"startDate": "2023-06-01T00:00:00.000Z", // Зависит от часового пояса сервера GAS
"targets": [
{
"region": " EMEA",
"budget": 5000
},
{
"region": "NA",
"budget": 10000
}
],
"metadata": {
"owner": "marketing@example.com",
"lastUpdated": "2023-10-27T10:30:00.000Z" // Текущая дата/время в ISO
}
}
*/
}Обработка особых случаев: циклические ссылки и функции
JSON.stringify() имеет ограничения:
Функции и undefined: Свойства со значениями-функциями или undefined пропускаются при сериализации. Если такие значения встречаются в массиве, они заменяются на null.
Циклические ссылки: Если объект содержит ссылки сам на себя, JSON.stringify() выбросит TypeError. Чтобы обойти это, можно использовать функцию replacer для обнаружения и обработки таких ссылок.
/**
* Демонстрация обработки функций и циклических ссылок.
*/
function handleSpecialCases() {
const obj = {
name: 'Test Object',
data: { value: 1 },
run: function() { console.log('Running...'); },
nothing: undefined
};
obj.self = obj; // Создаем циклическую ссылку
// Попытка прямой сериализации вызовет ошибку
try {
JSON.stringify(obj);
} catch (e) {
Logger.log(`Ошибка при прямой сериализации: ${e}`); // TypeError: Converting circular structure to JSON
}
// Использование replacer для удаления циклических ссылок и функций
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]'; // Заменяем циклическую ссылку
}
seen.add(value);
}
if (typeof value === 'function') {
return '[Function]'; // Заменяем функцию
}
if (typeof value === 'undefined') {
return '[Undefined]'; // Явно указываем undefined
}
return value;
};
};
const safeJsonString = JSON.stringify(obj, getCircularReplacer(), 2);
Logger.log(safeJsonString);
/* Вывод:
{
"name": "Test Object",
"data": {
"value": 1
},
"run": "[Function]",
"nothing": "[Undefined]",
"self": "[Circular]"
}
*/
}Другие методы преобразования объектов в строки
Хотя JSON.stringify() является стандартом, существуют и другие подходы.
Использование template literals (шаблонные литералы)
Шаблонные литералы (строки с обратными кавычками `) удобны для создания строк с динамическим содержимым, но не подходят для полной сериализации сложных объектов. Они полезны для форматированного вывода или создания простых строковых представлений.
/**
* Использование шаблонных литералов для простого строкового представления.
*/
function templateLiteralExample() {
const userData = {
name: 'Alice',
email: 'alice@example.com',
lastLogin: new Date()
};
const userString = `User: ${userData.name}, Email: ${userData.email}, Last Login: ${userData.lastLogin.toLocaleString()}`;
Logger.log(userString);
// Вывод: User: Alice, Email: alice@example.com, Last Login: 10/27/2023, 11:00:00 AM (формат зависит от локали)
}Ручное создание строки с помощью конкатенации
Этот метод наименее предпочтителен из-за сложности, подверженности ошибкам и плохой читаемости кода, особенно для сложных объектов. Используется крайне редко.
/**
* Пример ручной конкатенации (не рекомендуется).
*/
function manualConcatenation() {
const item = { id: 101, status: 'active' };
const itemString = 'ID: ' + item.id + '; Status: ' + item.status;
Logger.log(itemString); // Вывод: ID: 101; Status: active
}Применение Utilities.jsonStringify (если требуется совместимость со старым кодом)
В Google Apps Script существует также метод Utilities.jsonStringify(). Он функционально идентичен JSON.stringify(). Его использование может быть оправдано в старых проектах для единообразия или если требуется явное указание на использование утилиты GAS, но в новом коде предпочтительнее стандартный JSON.stringify().
/**
* Пример использования Utilities.jsonStringify.
*/
function utilitiesJsonStringifyExample() {
const data = { a: 1, b: [2, 3] };
const jsonString = Utilities.jsonStringify(data);
Logger.log(jsonString); // Вывод: "{"a":1,"b":[2,3]}"
}Особенности преобразования объектов Google Apps Script
Преобразование объектов, возвращаемых API Google Workspace (например, Sheets, Docs)
Объекты, возвращаемые сервисами GAS (например, Spreadsheet, Range, Document, Folder), часто являются сложными Java-объектами на стороне сервера с множеством методов, но ограниченным количеством сериализуемых свойств. Прямое применение JSON.stringify() к таким объектам обычно не дает полезного результата или возвращает пустой объект {}.
Вместо этого необходимо извлечь нужные данные с помощью методов этих объектов и поместить их в простой JavaScript-объект перед сериализацией.
/**
* Пример извлечения данных из объекта Range перед сериализацией.
*/
function serializeSheetData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
const range = sheet.getRange('A1:B2');
// Прямая сериализация объекта Range бесполезна
// Logger.log(JSON.stringify(range)); // Выведет {} или что-то неинформативное
// Извлекаем нужные данные
const data = {
values: range.getValues(),
formulas: range.getFormulas(),
sheetName: sheet.getName(),
rangeA1Notation: range.getA1Notation()
};
const jsonString = JSON.stringify(data, null, 2);
Logger.log(jsonString);
/* Примерный вывод:
{
"values": [
["Header 1", "Header 2"],
["Data A", "Data B"]
],
"formulas": [
["", ""],
["", ""]
],
"sheetName": "Sheet1",
"rangeA1Notation": "A1:B2"
}
*/
}Работа с датами и другими специфическими типами данных
Как упоминалось, JSON.stringify() автоматически преобразует объекты Date в строки формата ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ). При обратном преобразовании строки JSON в объект с помощью JSON.parse(), строки дат не преобразуются обратно в объекты Date автоматически. Это нужно делать вручную, например, с помощью параметра reviver в JSON.parse().
Другие специфические типы, такие как Map или Set, не сериализуются стандартным JSON.stringify() корректно. Их нужно предварительно преобразовать в массивы или объекты.
Обработка ошибок и исключений при преобразовании
Основная ошибка при использовании JSON.stringify() — это TypeError при наличии циклических ссылок. Важно использовать блоки try...catch при работе с потенциально сложными или неизвестными структурами данных, чтобы предотвратить аварийное завершение скрипта.
/**
* Обработка потенциальной ошибки при сериализации.
*
* @param {any} dataToSerialize Данные для сериализации.
* @return {string | null} Строка JSON или null в случае ошибки.
*/
function safeStringify(dataToSerialize) {
try {
// Используем replacer для предотвращения циклических ссылок
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return value;
};
};
return JSON.stringify(dataToSerialize, getCircularReplacer());
} catch (error) {
Logger.log(`Ошибка сериализации: ${error.message}`);
// Можно добавить более детальное логирование или обработку
console.error('Не удалось сериализовать объект:', error);
return null; // Возвращаем null или другое индикативное значение
}
}Примеры использования преобразованных строк
Преобразование объектов в строки находит применение во многих сценариях автоматизации.
Логирование данных в Google Cloud Logging
GAS интегрирован с Google Cloud Logging (ранее Stackdriver Logging). Использование console.log(), console.info(), console.warn(), console.error() позволяет отправлять структурированные логи.
/**
* Логирование структурированных данных в Cloud Logging.
*/
function logToCloud() {
const eventData = {
timestamp: new Date().toISOString(),
source: 'DataProcessingTrigger',
payload: { userId: 123, action: 'update', success: true },
executionTimeMs: 150
};
// Cloud Logging автоматически обработает объект, но явное использование
// console.log с объектом или строкой JSON гарантирует читаемость.
console.log({ message: 'Processing event', event: eventData });
// Можно также сначала преобразовать в строку, если нужно логгировать именно JSON
const eventString = JSON.stringify(eventData);
console.log(`Raw event JSON: ${eventString}`);
}Сохранение данных в Google Sheets или Google Docs
Строки JSON можно легко сохранять в ячейках Google Sheets или в тексте Google Docs для последующего извлечения и парсинга.
/**
* Сохранение конфигурации в ячейку Google Sheets.
*/
function saveConfigToSheet() {
const config = {
sheetId: 'abc123xyz',
syncEnabled: true,
lastSync: new Date().toISOString()
};
const configString = JSON.stringify(config);
const ss = SpreadsheetApp.getActiveSpreadsheet();
const configSheet = ss.getSheetByName('Configuration') || ss.insertSheet('Configuration');
configSheet.getRange('A1').setValue(configString);
Logger.log('Конфигурация сохранена в ячейку A1.');
}
/**
* Загрузка конфигурации из Google Sheets.
*
* @return {object | null} Разобранный объект конфигурации или null.
*/
function loadConfigFromSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const configSheet = ss.getSheetByName('Configuration');
if (!configSheet) return null;
const configString = configSheet.getRange('A1').getValue();
if (!configString) return null;
try {
const config = JSON.parse(configString);
// Опционально: преобразовать строки дат обратно в объекты Date
if (config.lastSync) {
config.lastSync = new Date(config.lastSync);
}
return config;
} catch (e) {
Logger.log(`Ошибка парсинга конфигурации: ${e}`);
return null;
}
}Отправка данных через API
При взаимодействии с внешними API часто требуется отправлять данные в формате JSON в теле POST-запроса.
/**
* Отправка данных о событии на внешний API.
*/
function sendDataToApi() {
const eventPayload = {
event: 'user_signup',
user_id: 'usr_789',
timestamp: new Date().toISOString(),
details: { source: 'webapp', referrer: 'google' }
};
const options = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(eventPayload), // Сериализуем объект в JSON
muteHttpExceptions: true // Чтобы обработать ошибки вручную
};
const url = 'https://api.example.com/events';
const response = UrlFetchApp.fetch(url, options);
Logger.log(`API Response Code: ${response.getResponseCode()}`);
Logger.log(`API Response Body: ${response.getContentText()}`);
}Передача данных между функциями и скриптами
При использовании PropertiesService для хранения данных между выполнениями скрипта или для обмена данными между разными скриптами (если они имеют доступ к одному и тому же хранилищу свойств), объекты необходимо сериализовать в строки.
/**
* Сохранение пользовательских настроек в PropertiesService.
*/
function saveUserSettings() {
const userSettings = {
theme: 'dark',
notifications: { email: true, push: false },
language: 'ru'
};
const settingsString = JSON.stringify(userSettings);
PropertiesService.getUserProperties().setProperty('userSettings', settingsString);
Logger.log('Настройки пользователя сохранены.');
}
/**
* Загрузка пользовательских настроек из PropertiesService.
*
* @return {object} Объект настроек пользователя.
*/
function loadUserSettings() {
const settingsString = PropertiesService.getUserProperties().getProperty('userSettings');
if (settingsString) {
try {
return JSON.parse(settingsString);
} catch (e) {
Logger.log(`Ошибка парсинга настроек: ${e}`);
// Возвращаем настройки по умолчанию
return { theme: 'light', notifications: { email: true, push: true }, language: 'en' };
}
} else {
// Настройки не найдены, возвращаем по умолчанию
return { theme: 'light', notifications: { email: true, push: true }, language: 'en' };
}
}Освоение методов преобразования объектов в строки, особенно JSON.stringify(), является ключевым навыком для эффективной разработки на Google Apps Script, позволяя гибко управлять данными при логировании, хранении и передаче.