При разработке функционала для работы с маркированными товарами (система «Честный ЗНАК») программисты часто сталкиваются с ошибкой преобразования данных 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.
Рассмотрим самый надежный способ, который позволяет передать строку в неизменном виде, обходя ограничения XML-сериализации. Суть метода заключается в преобразовании строки в двоичные данные, а затем в Base64-строку. Формат Base64 состоит только из разрешенных символов латиницы и цифр, поэтому XDTO пропустит его без замечаний.
Разберем по шагам реализацию на стороне клиента:
// Код на стороне клиента
&НаКлиенте
Процедура ОбработатьКодМаркировки(ВыбранныйКод)
// Преобразуем строку в двоичные данные, используя кодировку UTF-8 (или ANSI)
ДвоичныеДанныеКода = ПолучитьДвоичныеДанныеИзСтроки(ВыбранныйКод, КодировкаТекста.UTF8);
// Кодируем в Base64 для безопасной передачи через XDTO
БезопаснаяСтрока = Base64Строка(ДвоичныеДанныеКода);
// Передаем на сервер уже защищенную строку
ОбработатьНаСервере(БезопаснаяСтрока);
КонецПроцедуры
Теперь проанализируем обратное преобразование на стороне сервера:
// Код на стороне сервера
&НаСервере
Процедура ОбработатьНаСервере(БезопаснаяСтрока)
// Декодируем Base64 обратно в двоичные данные
ДвоичныеДанныеСервер = Base64Значение(БезопаснаяСтрока);
// Получаем исходную строку со всеми спецсимволами (включая код 29)
ИсходныйКодМаркировки = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанныеСервер, КодировкаТекста.UTF8);
// Теперь переменную ИсходныйКодМаркировки можно использовать для парсинга или записи
// ...
КонецПроцедуры
Этот метод является наиболее «чистым», так как он не искажает содержимое кода маркировки, что критически важно для дальнейшей проверки кода в системе ГИС МТ. Для практической реализации этого подхода можно использовать обработку для декодирования кодов маркировки из Base64.
Если нам необходимо передать не одну строку, а целую таблицу значений или массив, содержащий проблемные коды, можно воспользоваться механизмом временного хранилища. Объекты, помещенные во временное хранилище, не сериализуются в XML при вызове серверного метода — на сервер передается только строковый адрес-ссылка.
Посмотрим на пример реализации:
&НаКлиенте
Процедура ПередатьЧерезХранилище()
// Помещаем массив строк с кодами маркировки во временное хранилище
АдресХранилища = ПоместитьВоВременноеХранилище(МассивКодовМаркировки, ЭтаФорма.УникальныйИдентификатор);
// Передаем только адрес
ОбработатьДанныеИзХранилищаНаСервере(АдресХранилища);
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеИзХранилищаНаСервере(АдресХранилища)
// Извлекаем данные на сервере
Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
Для Каждого ТекущийКод Из Данные Цикл
// Здесь символ с кодом 29 будет сохранен
ПроверитьКод(ТекущийКод);
КонецЦикла;
КонецПроцедуры
Важный нюанс: использование временного хранилища создает нагрузку на память сервера или сеансовые данные, поэтому для передачи одиночных небольших строк лучше использовать первый способ.
Рассмотрим альтернативный алгоритм, предложенный участниками обсуждения. Он заключается в разложении строки на массив чисел (кодов ASCII каждого символа). Это гарантирует, что XML-парсер увидит только числа и пробелы, которые разрешены.
Проанализируем реализацию функции кодирования:
&НаКлиенте
Функция ЗакодироватьСтрокуВКоды(ИсходнаяСтрока)
МассивКодов = Новый Массив;
Для Инд = 1 По СтрДлина(ИсходнаяСтрока) Цикл
МассивКодов.Добавить(КодСимвола(Сред(ИсходнаяСтрока, Инд, 1)));
КонецЦикла;
// Соединяем коды через пробел в одну строку
Возврат СтрСоединить(МассивКодов, " ");
КонецФункции
На сервере выполним обратную сборку строки:
&НаСервере
Функция ДекодироватьСтрокуИзКодов(СтрокаКодов)
Результат = "";
МассивКодов = СтрРазделить(СтрокаКодов, " ", Ложь);
Для Каждого ТекКод Из МассивКодов Цикл
Результат = Результат + Символ(Число(ТекКод));
КонецЦикла;
Возврат Результат;
КонецФункции
Хотя этот метод рабочий, он значительно увеличивает объем передаваемых данных (каждый символ превращается в 2-3 цифры плюс пробел), поэтому Base64 остается более предпочтительным.
Проанализируем типичную ошибку: использование функции СтрЗаменить(КодМаркировки, " ", "") или попытку «очистить» строку от спецсимволов.
GS, структура кода маркировки будет нарушена. Серверные библиотеки для парсинга (например, api-is-mark) не смогут корректно разделить идентификаторы применения (AI), и код будет признан невалидным.Символ(29) и " " (код 32) — это разные вещи. Обычная замена через СтрЗаменить не поможет, если вы ищете символ пробела.Для успешной передачи данных со спецсимволами следуйте этим правилам:
КодСимвола(), если подозреваете наличие «невидимых» знаков.Base64Строка() для безопасной упаковки данных перед вызовом серверных методов.ДвоичныеДанные, передавайте их напрямую — платформа умеет корректно сериализовать этот тип данных.Применяя метод Base64-кодирования, вы гарантируете стабильную работу обмена данными между клиентом и сервером без риска возникновения ошибок XDTO. Для автоматизации декодирования кодов в документах заказа на эмиссию подойдёт декодер Base64-кодов маркировки.