Экранирование JSON: кавычки, обратная черта и Unicode без путаницы

Проверяем JSON escapes, отличаем перевод строки от буквального \n и разбираем emoji как Unicode code point, а не два символа текста.

Лента проходит через стеклянные призмы как образ кодирования строк и Unicode

Текст \n может означать перевод строки или два обычных символа — обратную черту и букву n. Различие зависит от слоя: исходный JSON, разобранное значение или строка программы, которая содержит JSON. Без разделения этих слоёв легко добавить лишнее экранирование или удалить нужное.

В JSON строка окружена двойными кавычками. Внутренние кавычки, обратная черта и управляющие символы требуют escape; Unicode может быть записан непосредственно или через \uXXXX. Для знака вне BMP используется пара UTF-16 escapes. RFC 8259, раздел 7.

Один документ с четырьмя видами строк

Синтетический ввод для JSON validator Neraviko:

{"path":"C:\\demo\\file","quote":"say \"hi\"","line":"first\nsecond","emoji":"\uD83D\uDE00"}

После разбора path содержит по одной обратной черте между частями пути, quote — обычные кавычки вокруг hi, line — реальный перевод строки между словами. Значение emoji — 😀. Запись пути используется только как текст; инструмент не открывает файл по этому имени.

Для проверки в Node.js выполните следующий пример. String.raw сохраняет обратные черты в исходнике, чтобы JavaScript не обработал их до JSON-парсера:

const source = String.raw`{"path":"C:\\demo\\file","quote":"say \"hi\"","line":"first\nsecond","emoji":"\uD83D\uDE00"}`;
const data = JSON.parse(source);
console.log(JSON.stringify(data.path));
console.log(data.emoji.length);
console.log([...data.emoji].length);
console.log(data.line.split("\n").length);

Проверенный вывод:

"C:\\demo\\file"
2
1
2

Первая строка снова сериализует путь в JSON, поэтому обратные черты на экране удвоены. Это описание значения, а не удвоение самих разделителей пути. У emoji длина JavaScript-строки равна двум UTF-16 code units; итерация по code points даёт один элемент. Это ещё не универсальный способ считать видимые символы: составные emoji и комбинируемые знаки могут включать несколько code points.

Буквальное «обратная черта + n»

Сравните два отдельных документа:

{"text":"\n"}
{"text":"\\n"}

Первый содержит перевод строки после разбора. Второй — буквальный текст \n. Проверяем без визуального угадывания:

const newline = JSON.parse(String.raw`{"text":"\n"}`).text;
const literal = JSON.parse(String.raw`{"text":"\\n"}`).text;
console.log(newline.length, literal.length);
console.log(newline.charCodeAt(0), literal.charCodeAt(0));

Вывод — 1 2 и 10 92. Поэтому при диагностике полезны кодовые значения и явная сериализация, а не только то, как редактор рисует строку. Не удаляйте все обратные черты, чтобы сделать текст «читаемым»: вы измените значение или получите невалидный документ.

У строки есть ещё один размер — байты кодировки. Показанный emoji занимает четыре байта в UTF-8, хотя JavaScript сообщает две UTF-16 code units. Размер файла, длина строки и число видимых знаков поэтому не обязаны совпадать. При byte position ошибки Neraviko ищите место в исходном UTF-8 тексте; не умножайте количество знаков на постоянный коэффициент, потому что разные знаки имеют разную длину.

JSON и строка JavaScript не взаимозаменяемы

Запись \u{1F600} допустима в некоторых контекстах JavaScript, но не является JSON escape. Для JSON используйте сам знак или \uD83D\uDE00. Одиночная половина пары, например "\uD83D", отклоняется текущим парсером Neraviko. Его проверка включает парность surrogate escapes, а не только расстановку скобок.

Не копируйте в JSON любые escapes из CSS, Python или URL. У форматов разные правила. Для генерации тела запроса в программе используйте штатный сериализатор соответствующего языка, передавая ему значения. Ручная сборка через вставку произвольного пользовательского текста между кавычками ненадёжна.

Как исследовать двойное экранирование

Начните с источника: у вас готовый JSON-документ или строковое поле, в котором лежит ещё один JSON? Пример {"payload":"{\"ready\":true}"} синтаксически корректен, но payload здесь строка. Дополнительный разбор допустим только если контракт явно говорит о вложенном JSON-тексте.

Затем посмотрите результат первого разбора и тип нужного поля. Если требуется объект, исправляйте сериализацию отправителя. Если требуется строка, экранирование внутренних кавычек ожидаемо. Повторное применение stringify к уже сериализованному документу как к строке создаёт ещё один слой; удаление escapes получателем маскирует эту ошибку.

Форматтер Neraviko сохраняет исходное написание строковых escapes. Поэтому \u0041 не обязан превратиться в A в отформатированном результате, хотя при декодировании это одно значение. Форматтер не заявляет отдельный режим unescape или преобразования Unicode.

Обработка в Neraviko серверная; передавайте искусственные строки вместо рабочих секретов. JSON-операции имеют лимит 262 144 байта запроса. Если нужна проверка конкретного escape, сократите документ до одного поля и сохраните его исходный текст: по снимку экрана число обратных черт определить сложнее.