|
Какие недопустимые символы могут быть в JSON?
В JSON существуют строгие правила для допустимых символов. Вот полный список недопустимых символов и правил:
1. Управляющие символы (Control Characters)
Недопустимы в сыром виде, должны экранироваться:
`\0` - Null character `\b` - Backspace `\t` - Tab `\n` - Line feed `\f` - Form feed `\r` - Carriage return `\u` - Символы Unicode должны быть экранированы как `\uXXXX`
2. Специальные символы в строках
Должны экранироваться обратным слешем:
`"` - Двойная кавычка (должна быть `\"`) `\` - Обратный слеш (должен быть `\\`) `/` - Прямой слеш (может быть экранирован как `\/`, но не обязательно)
3. Недопустимые символы в строках
Эти символы вообще не разрешены в JSON строках:
- Непечатные ASCII символы (коды 0-31), кроме тех что экранируются - `\x7F` (DEL) - Любые символы не в кодировке UTF-8 (если используется UTF-8)
4. Недопустимые символы в числах
- Пробелы внутри числа - Множественные десятичные точки (например `1.2.3`) - Ведущие нули (например `0123`) - не разрешены в некоторых парсерах - Знаки `+`, `-` только в начале числа - `,` как разделитель тысяч
5. Структурные ограничения
Недопустимо:
- Запятая после последнего элемента массива/объекта: `[1,2,3,]` - Пропущенные значения: `[1,,3]` - Комментарии (`//`, `/* */`) - не разрешены в стандартном JSON - Одинарные кавычки вместо двойных: `{'key': 'value'}`
6. Примеры недопустимого JSON
// Недопустимо:
{
name: "John", // Ключ без кавычек
'age': 30, // Одинарные кавычки
"city": "New\nYork", // Неэкранированный \n
"data": undefined, // JavaScript undefined
"bytes": 0x1F, // Шестнадцатеричное число
"leading-zero": 0123, // Ведущий ноль
"trailing-comma": [1,2,3,], // Запятая в конце
"comment": /* comment */ "text" // Комментарий
}
7. Допустимые символы в строках JSON
Любой символ Unicode, кроме управляющих, должен быть представлен либо напрямую (если парсер поддерживает UTF-8), либо экранирован:
- `\u0444` - кириллическая 'ф' - `\u20AC` - символ евро (€) - `\u00A9` - символ копирайта (©)
8. Правильное экранирование
{ "valid": "Это строка с \n новой строкой", "quotes": "Строка с \"кавычками\"", "backslash": "Путь: C:\\Program Files", "unicode": "\u041F\u0440\u0438\u0432\u0435\u0442"
}
При работе с JSON всегда важно убедиться, что все строки правильно экранированы, особенно при включении пользовательского ввода или данных из внешних источников.
[Экранирование специальных символов в JSON]
Специальные символы в JSON наподобие '\n', '\r' и т. п. требуют экранирования. Для этого можно использовать следующие функции.
// JavaScript: вспомогательная функция для экранирования // специальных символов в строке: const escapeJSONString = (str) => { if (!str) return str
return str .replace(/\\/g, '\\\\') // Обратный слеш .replace(/"/g, '\\"') // Двойная кавычка .replace(/\n/g, '\\n') // Перевод строки .replace(/\r/g, '\\r') // Возврат каретки .replace(/\t/g, '\\t') // Табуляция .replace(/\b/g, '\\b') // Backspace .replace(/\f/g, '\\f') // Form feed
}
// JavaScript: вспомогательная функция для восстановления // экранированных символов const unescapeJSONString = (str) => { if (!str) return str
return str .replace(/\\n/g, '\n') .replace(/\\r/g, '\r') .replace(/\\t/g, '\t') .replace(/\\b/g, '\b') .replace(/\\f/g, '\f') .replace(/\\"/g, '"') .replace(/\\\\/g, '\\')
}
Аналогичные функции на языке C:
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < ctype.h>
/** * Экранирует специальные символы в строке для JSON * @param src Исходная строка * @param dst Буфер для результата (должен быть достаточно большим) * @param dst_size Размер буфера назначения * @return Количество записанных символов (без учёта null-терминатора) * или -1 при ошибке */ int escape_json_string(const char* src, char* dst, size_t dst_size) { if (!src || !dst || dst_size == 0) { return -1; }
size_t src_len = strlen(src); size_t dst_pos = 0;
for (size_t i = 0; i < src_len && dst_pos < dst_size - 1; i++) { char ch = src[i];
switch (ch) { case '\\': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = '\\'; break; case '"': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = '"'; break; case '\n': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = 'n'; break; case '\r': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = 'r'; break; case '\t': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = 't'; break; case '\b': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = 'b'; break; case '\f': if (dst_pos + 2 >= dst_size) return -1; dst[dst_pos++] = '\\'; dst[dst_pos++] = 'f'; break; default: dst[dst_pos++] = ch; break; } }
dst[dst_pos] = '\0'; return (int)dst_pos;
}
/** * Восстанавливает экранированные символы в строке * @param src Исходная строка * @param dst Буфер для результата (должен быть достаточно большим) * @param dst_size Размер буфера назначения * @return Количество записанных символов (без учёта null-терминатора) * или -1 при ошибке */ int unescape_json_string(const char* src, char* dst, size_t dst_size) { if (!src || !dst || dst_size == 0) { return -1; }
size_t src_len = strlen(src); size_t dst_pos = 0;
for (size_t i = 0; i < src_len && dst_pos < dst_size - 1; i++) { char ch = src[i];
if (ch == '\\' && i + 1 < src_len) { char next_ch = src[i + 1];
switch (next_ch) { case 'n': dst[dst_pos++] = '\n'; i++; break; case 'r': dst[dst_pos++] = '\r'; i++; break; case 't': dst[dst_pos++] = '\t'; i++; break; case 'b': dst[dst_pos++] = '\b'; i++; break; case 'f': dst[dst_pos++] = '\f'; i++; break; case '"': dst[dst_pos++] = '"'; i++; break; case '\\': dst[dst_pos++] = '\\'; i++; break; default: dst[dst_pos++] = ch; break; } } else { dst[dst_pos++] = ch; } }
dst[dst_pos] = '\0'; return (int)dst_pos;
}
/** * Выделяет память и возвращает экранированную строку * @param src Исходная строка * @return Указатель на новую строку в куче или NULL при ошибке * Примечание: caller отвечает за освобождение памяти через free() */ char* escape_json_string_alloc(const char* src) { if (!src) return NULL;
// Максимальный размер: каждый символ может превратиться в 2 символа size_t max_len = strlen(src) * 2 + 1; char* result = (char*)malloc(max_len);
if (!result) return NULL;
if (escape_json_string(src, result, max_len) < 0) { free(result); return NULL; }
return result;
}
/** * Выделяет память и возвращает восстановленную строку * @param src Исходная строка * @return Указатель на новую строку в куче или NULL при ошибке * Примечание: caller отвечает за освобождение памяти через free() */ char* unescape_json_string_alloc(const char* src) { if (!src) return NULL;
size_t len = strlen(src); char* result = (char*)malloc(len + 1);
if (!result) return NULL;
if (unescape_json_string(src, result, len + 1) < 0) { free(result); return NULL; }
return result;
}
/** * Версия функции, которая изменяет строку на месте (in-place) * @param str Строка для обработки (должна быть достаточно большой) * @return Указатель на ту же строку или NULL при ошибке */ char* escape_json_string_inplace(char* str) { if (!str) return NULL;
size_t len = strlen(str); char* temp = (char*)malloc(len * 2 + 1);
if (!temp) return NULL;
if (escape_json_string(str, temp, len * 2 + 1) < 0) { free(temp); return NULL; }
strcpy(str, temp); free(temp);
return str;
}
/** * Версия функции, которая изменяет строку на месте (in-place) * @param str Строка для обработки * @return Указатель на ту же строку или NULL при ошибке */ char* unescape_json_string_inplace(char* str) { if (!str) return NULL;
size_t len = strlen(str); char* temp = (char*)malloc(len + 1);
if (!temp) return NULL;
if (unescape_json_string(str, temp, len + 1) < 0) { free(temp); return NULL; }
strcpy(str, temp); free(temp);
return str;
}
|