Как прочитать JSON в 1С без ошибок преобразования Unicode и управляющих символов?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции систем 1С с внешними веб-сервисами разработчики регулярно сталкиваются с обработкой формата JSON. Встроенные механизмы платформы, такие как объект ЧтениеJSON и функция ПрочитатьJSON, отлично справляются со стандартными задачами. Однако при появлении в тексте управляющих Unicode-последовательностей (особенно нулевого символа \u0000 или символов диапазона от \u0001 до \u001F) система может повести себя непредсказуемо. Самая частая проблема — успешное чтение JSON на сервере и последующее критическое падение платформы при попытке передать полученную структуру на клиент.

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем внутренние механизмы платформы 1С и рассмотрим три эффективных практических способа обхода данной проблемы.

Почему возникает ошибка и почему её нельзя отключить в настройках ЧтенияJSON?

Согласно международному стандарту RFC 8259, описывающему формат JSON, парсер обязаны преобразовывать любые экранированные последовательности вида \uXXXX в соответствующие им символы Unicode. В платформе «1С:Предприятие» встроенный парсер жестко следует этому стандарту. В объекте ЧтениеJSON нет и не может быть настроек, отключающих декодирование Unicode-символов.

Когда парсер встречает, например, последовательность \u0000, он преобразует её в реальный символ с кодом 0 (null-byte). Сама по себе десериализация на сервере проходит без ошибок, и мы получаем объект типа Структура или Соответствие. Проблемы начинаются при попытке передать этот объект с сервера на клиент (в тонкий или веб-клиент).

Давайте выясним техническую причину падения:

  1. XML-сериализация под капотом: Любой обмен данными между контекстом сервера и клиента в управляемом приложении 1С выполняется посредством сериализации объектов в формат XML (через механизмы XDTO).
  2. Ограничения стандарта XML: Согласно спецификации XML 1.0, управляющие символы (такие как символ с кодом 0, символ с кодом 8 (Backspace) и другие) являются строго недопустимыми. Они физически не могут присутствовать в XML-документе.
  3. Аварийное завершение: Как только сериализатор 1С встречает в строковом значении структуры недопустимый символ XML, платформа выбрасывает исключение: «Ошибка передачи данных между клиентом и сервером» или «Ошибка преобразования данных XDTO».

Кроме того, в языках низкого уровня (таких как C++, на котором написана платформа 1С) символ с кодом 0 является маркером конца строки. Его появление внутри строки 1С может приводить к неожиданному обрезанию данных или нестабильной работе рабочих процессов сервера.

Анализ популярных заблуждений

Часто на форумах предлагают использовать встроенную функцию НайтиНедопустимыеСимволыXML() прямо на сыром тексте ответа от сервера перед его парсингом. Рассмотрим пример такого ошибочного кода:


// ВНИМАНИЕ: Этот код НЕ сработает для строки "\u0000"!
СтрокаОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
Позиция = НайтиНедопустимыеСимволыXML(СтрокаОтвета); // Результат будет 0

Почему это не работает? В исходном JSON-тексте, полученном от сервера, последовательность \u0000 состоит из шести обычных печатных символов: обратного слэша, буквы «u» и четырех нулей. Для функции НайтиНедопустимыеСимволыXML() это абсолютно легальный текст. Символ становится недопустимым «нулевым байтом» только после того, как отработает метод ПрочитатьJSON(). Следовательно, искать недопустимые XML-символы в сыром JSON-тексте до его парсинга бессмысленно.

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

Решение 1: Предобработка сырого JSON-текста (Самый простой и быстрый способ)

Если нам заранее известно, что внешний сервис может прислать нежелательные управляющие символы (чаще всего это именно \u0000), самым надежным решением будет вырезать или заменить эти текстовые последовательности в строке до момента вызова парсера.

Поскольку обратный слэш в строковых литералах 1С является спецсимволом, для корректного поиска подстроки \u0000 мы должны использовать двойное экранирование слэша. Рассмотрим пример кода:


// Получаем JSON-текст из HTTP-ответа или файла
СтрокаJSON = HTTPОтвет.ПолучитьТелоКакСтроку();

// Удаляем текстовое представление "\u0000" из JSON-строки
СтрокаJSON = СтрЗаменить(СтрокаJSON, "\\u0000", "");

// Или, если необходимо сохранить признак пустого значения для логики:
СтрокаJSON = СтрЗаменить(СтрокаJSON, "\\u0000", "[NULL_CHAR]");

// Теперь можно безопасно парсить строку
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
РезультатДесериализации = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();

Если на входе может быть целый спектр управляющих символов (от \u0000 до \u001F), регулярные выражения помогут нам очистить строку более элегантно. Проанализируем регулярное выражение для удаления таких последовательностей:


// Пример очистки с использованием COM-объекта RegExp (для Windows-серверов)
РегВыражение = Новый COMОбъект("VBScript.RegExp");
РегВыражение.Pattern = "\\u00[0-1][0-9a-fA-F]"; // Находит паттерны от \u0000 до \u001F
РегВыражение.Global = Истина;

СтрокаJSON = РегВыражение.Replace(СтрокаJSON, "");

Решение 2: Рекурсивный обход и очистка прочитанной структуры на сервере

Что делать, если JSON уже успешно прочитан на сервере во внутренние объекты 1С (коллекции Структура, Соответствие или Массив), и нам нужно гарантировать их безопасную передачу на клиент? В этом случае мы должны написать рекурсивную процедуру, которая обойдет всю коллекцию и удалит недопустимые XML-символы из всех строковых значений.

Давайте разработаем универсальный механизм очистки объектов:


// Функция очищает конкретную строку от недопустимых символов XML
Функция ОчиститьСтрокуОтНедопустимыхСимволов(Знач ВходнаяСтрока) Экспорт
    
    Позиция = НайтиНедопустимыеСимволыXML(ВходнаяСтрока);
    Пока Позиция <> 0 Цикл
        // Вырезаем битый символ
        ВходнаяСтрока = Лев(ВходнаяСтрока, Позиция - 1) + Сред(ВходнаяСтрока, Позиция + 1);
        Позиция = НайтиНедопустимыеСимволыXML(ВходнаяСтрока);
    КонецЦикла;
    
    Возврат ВходнаяСтрока;
    
КонецФункции

// Рекурсивная процедура для обхода вложенных коллекций 1С
Процедура ОчиститьДанныеОтНедопустимыхСимволов(Коллекция) Экспорт
    
    Если ТипЗнч(Коллекция) = Тип("Структура") Тогда
        
        Для Каждого Элемент Из Коллекция Цикл
            Если ТипЗнч(Элемент.Значение) = Тип("Строка") Тогда
                Коллекция[Элемент.Ключ] = ОчиститьСтрокуОтНедопустимыхСимволов(Элемент.Значение);
            Иначе
                ОчиститьДанныеОтНедопустимыхСимволов(Элемент.Значение);
            КонецЕсли;
        КонецЦикла;
        
    ИначеЕсли ТипЗнч(Коллекция) = Тип("Соответствие") Тогда
        
        КлючиДляИзменения = Новый Массив;
        Для Каждого Элемент Из Коллекция Цикл
            Если ТипЗнч(Элемент.Значение) = Тип("Строка") Или ТипЗнч(Элемент.Ключ) = Тип("Строка") Тогда
                КлючиДляИзменения.Добавить(Элемент.Ключ);
            Иначе
                ОчиститьДанныеОтНедопустимыхСимволов(Элемент.Значение);
            КонецЕсли;
        КонецЦикла;
        
        // Перезаписываем строковые ключи и значения в Соответствии
        Для Каждого Ключ Из КлючиДляИзменения Цикл
            Значение = Коллекция[Ключ];
            НовыйКлюч = Ключ;
            Если ТипЗнч(Ключ) = Тип("Строка") Тогда
                НовыйКлюч = ОчиститьСтрокуОтНедопустимыхСимволов(Ключ);
            КонецЕсли;
            Если ТипЗнч(Значение) = Тип("Строка") Тогда
                Значение = ОчиститьСтрокуОтНедопустимыхСимволов(Значение);
            КонецЕсли;
            
            Если НовыйКлюч <> Ключ Тогда
                Коллекция.Удалить(Ключ);
            КонецЕсли;
            Коллекция.Вставить(НовыйКлюч, Значение);
        КонецЦикла;
        
    ИначеЕсли ТипЗнч(Коллекция) = Тип("Массив") Тогда
        
        Для Индекс = 0 По Коллекция.ВГраница() Цикл
            Если ТипЗнч(Коллекция[Индекс]) = Тип("Строка") Тогда
                Коллекция[Индекс] = ОчиститьСтрокуОтНедопустимыхСимволов(Коллекция[Индекс]);
            Иначе
                ОчиститьДанныеОтНедопустимыхСимволов(Коллекция[Индекс]);
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;
    
КонецПроцедуры

Этот метод гарантирует, что структура любой вложенности будет полностью пригодна для передачи между сервером и клиентом без риска падения сеанса работы пользователя.

Решение 3: Использование формата Base64 для транзита «сырых» данных

Бывают ситуации, когда бизнес-логика приложения строго требует получить на стороне клиента строку в её исходном, «первозданном» виде, включая все спецсимволы и бинарные маркеры. Поскольку стандартный тип Строка передать не удастся из-за XML-сериализации, мы можем использовать кодирование данных в Base64.

Рассмотрим, как это реализовать:

  1. На сервере мы считываем исходный JSON в строку.
  2. Вместо десериализации или передачи "грязной" строки, мы преобразуем её в двоичные данные, а затем кодируем в безопасную Base64-строку.
  3. Передаем Base64-строку на клиент. На клиенте не произойдет ошибки XDTO, так как Base64 содержит исключительно разрешенные ASCII-символы.

Посмотрим на пример кода реализации данного подхода:


// НА СЕРВЕРЕ:
Функция ПолучитьБезопаснуюСтрокуJSON(ПутьКФайлу) Экспорт
    
    // Читаем сырой JSON как двоичные данные для сохранения всех байтов
    ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
    СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ДвоичныеДанные);
    
    Возврат СтрокаBase64; // Абсолютно безопасный транзит на клиент
    
КонецФункции

// НА КЛИЕНТЕ:
Процедура ОбработатьJSONНаКлиенте(СтрокаBase64)
    
    ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64Строки(СтрокаBase64);
    // На клиенте мы можем сохранить данные в файл или передать во внешнюю 
    // компоненту (например, парсер JS на веб-странице), минуя ограничения XML 1С
    ДвоичныеДанные.Записать(КаталогВременныхФайлов() + "response.json");
    
КонецПроцедуры

Подведем итоги

Мы проанализировали проблему работы с Unicode-последовательностями в JSON-структурах 1С и выяснили, что платформа не содержит штатной возможности отключить декодирование Unicode при чтении JSON. Ошибки при передаче данных на клиент возникают из-за несовместимости полученных спецсимволов со стандартом XML 1.0.

Для решения этой проблемы выбирайте наиболее подходящий под ваши задачи инструмент:

← На главную