Как исправить ошибку XDTO при передаче кода маркировки с клиента на сервер в 1С?

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

При разработке функционала для работы с маркированными товарами (система «Честный ЗНАК») программисты часто сталкиваются с ошибкой преобразования данных XDTO при попытке передать строку кода маркировки с клиента на сервер. Ошибка обычно звучит так: «Текст XML содержит недопустимый символ в позиции...». Проанализируем причины этой ситуации и разберем наиболее эффективные способы её решения.

Выясним причину возникновения ошибки

Проблема кроется в особенностях работы платформы 1С:Предприятие при передаче данных между клиентским и серверным контекстами. Когда мы вызываем серверную процедуру и передаем в неё параметры (или когда данные формы автоматически синхронизируются), платформа выполняет сериализацию данных в формат XML через механизмы XDTO.

Стандарт XML 1.0 накладывает жесткие ограничения на используемые символы. В частности, в XML-документах запрещено использование большинства управляющих символов ASCII с кодами от 0 до 31. Единственными исключениями являются символы с кодами 9 (табуляция), 10 (перевод строки) и 13 (возврат каретки).

В кодах маркировки формата DataMatrix (GS1) обязательным элементом является символ-разделитель групп данных GS (Group Separator), имеющий код 29. Поскольку 29 входит в диапазон запрещенных символов, стандартный парсер XML выдает ошибку, как только встречает его в строковом значении параметра v или param.

Способ 1. Использование Base64-кодирования (Рекомендуемый)

Рассмотрим самый надежный способ, который позволяет передать строку в неизменном виде, обходя ограничения XML-сериализации. Суть метода заключается в преобразовании строки в двоичные данные, а затем в Base64-строку. Формат Base64 состоит только из разрешенных символов латиницы и цифр, поэтому XDTO пропустит его без замечаний.

Разберем по шагам реализацию на стороне клиента:


// Код на стороне клиента
&НаКлиенте
Процедура ОбработатьКодМаркировки(ВыбранныйКод)
    
    // Преобразуем строку в двоичные данные, используя кодировку UTF-8 (или ANSI)
    ДвоичныеДанныеКода = ПолучитьДвоичныеДанныеИзСтроки(ВыбранныйКод, КодировкаТекста.UTF8);
    
    // Кодируем в Base64 для безопасной передачи через XDTO
    БезопаснаяСтрока = Base64Строка(ДвоичныеДанныеКода);
    
    // Передаем на сервер уже защищенную строку
    ОбработатьНаСервере(БезопаснаяСтрока);
    
КонецПроцедуры

Теперь проанализируем обратное преобразование на стороне сервера:


// Код на стороне сервера
&НаСервере
Процедура ОбработатьНаСервере(БезопаснаяСтрока)
    
    // Декодируем Base64 обратно в двоичные данные
    ДвоичныеДанныеСервер = Base64Значение(БезопаснаяСтрока);
    
    // Получаем исходную строку со всеми спецсимволами (включая код 29)
    ИсходныйКодМаркировки = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанныеСервер, КодировкаТекста.UTF8);
    
    // Теперь переменную ИсходныйКодМаркировки можно использовать для парсинга или записи
    // ...
    
КонецПроцедуры

Этот метод является наиболее «чистым», так как он не искажает содержимое кода маркировки, что критически важно для дальнейшей проверки кода в системе ГИС МТ. Для практической реализации этого подхода можно использовать обработку для декодирования кодов маркировки из Base64.

Способ 2. Использование временного хранилища

Если нам необходимо передать не одну строку, а целую таблицу значений или массив, содержащий проблемные коды, можно воспользоваться механизмом временного хранилища. Объекты, помещенные во временное хранилище, не сериализуются в XML при вызове серверного метода — на сервер передается только строковый адрес-ссылка.

Посмотрим на пример реализации:


&НаКлиенте
Процедура ПередатьЧерезХранилище()
    
    // Помещаем массив строк с кодами маркировки во временное хранилище
    АдресХранилища = ПоместитьВоВременноеХранилище(МассивКодовМаркировки, ЭтаФорма.УникальныйИдентификатор);
    
    // Передаем только адрес
    ОбработатьДанныеИзХранилищаНаСервере(АдресХранилища);
    
КонецПроцедуры

&НаСервере
Процедура ОбработатьДанныеИзХранилищаНаСервере(АдресХранилища)
    
    // Извлекаем данные на сервере
    Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Для Каждого ТекущийКод Из Данные Цикл
        // Здесь символ с кодом 29 будет сохранен
        ПроверитьКод(ТекущийКод);
    КонецЦикла;
    
КонецПроцедуры

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

Способ 3. Преобразование в массив кодов символов

Рассмотрим альтернативный алгоритм, предложенный участниками обсуждения. Он заключается в разложении строки на массив чисел (кодов ASCII каждого символа). Это гарантирует, что XML-парсер увидит только числа и пробелы, которые разрешены.

Проанализируем реализацию функции кодирования:


&НаКлиенте
Функция ЗакодироватьСтрокуВКоды(ИсходнаяСтрока)
    
    МассивКодов = Новый Массив;
    Для Инд = 1 По СтрДлина(ИсходнаяСтрока) Цикл
        МассивКодов.Добавить(КодСимвола(Сред(ИсходнаяСтрока, Инд, 1)));
    КонецЦикла;
    
    // Соединяем коды через пробел в одну строку
    Возврат СтрСоединить(МассивКодов, " ");
    
КонецФункции

На сервере выполним обратную сборку строки:


&НаСервере
Функция ДекодироватьСтрокуИзКодов(СтрокаКодов)
    
    Результат = "";
    МассивКодов = СтрРазделить(СтрокаКодов, " ", Ложь);
    
    Для Каждого ТекКод Из МассивКодов Цикл
        Результат = Результат + Символ(Число(ТекКод));
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

Хотя этот метод рабочий, он значительно увеличивает объем передаваемых данных (каждый символ превращается в 2-3 цифры плюс пробел), поэтому Base64 остается более предпочтительным.

Чего НЕ следует делать

Проанализируем типичную ошибку: использование функции СтрЗаменить(КодМаркировки, " ", "") или попытку «очистить» строку от спецсимволов.

  1. Удаление символа с кодом 29: Если вы просто удалите символ GS, структура кода маркировки будет нарушена. Серверные библиотеки для парсинга (например, api-is-mark) не смогут корректно разделить идентификаторы применения (AI), и код будет признан невалидным.
  2. Замена на пробел: Символ 29 часто отображается в отладчике 1С как пустой квадрат или пробел, но Символ(29) и " " (код 32) — это разные вещи. Обычная замена через СтрЗаменить не поможет, если вы ищете символ пробела.

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

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

Применяя метод Base64-кодирования, вы гарантируете стабильную работу обмена данными между клиентом и сервером без риска возникновения ошибок XDTO. Для автоматизации декодирования кодов в документах заказа на эмиссию подойдёт декодер Base64-кодов маркировки.

← На главную