Недопустимые символы в JSON Печать
Добавил(а) microsin   

Какие недопустимые символы могут быть в 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; }