При интеграции 1С с внешним оборудованием (лабораторными анализаторами, кассовой техникой, сканерами или промышленными контроллерами) разработчики нередко сталкиваются с ситуацией, когда данные из файла или COM-порта успешно считываются в строковую переменную, отображаются в отладчике, но при попытке записать объект в базу возникает исключение: «Недопустимый символ XML».
Проанализируем природу этой ошибки, выясним причину ее возникновения и подробно разберем все способы решения проблемы.
Символ STX (Start of Text, шестнадцатеричный код 0x02, десятичный 2) является стандартным служебным символом кадрирования во многих протоколах передачи данных (например, ASTM E1381/E1394, HL7). Если парсер оборудования передает в прикладные реквизиты сырой поток данных без предварительной очистки служебных заголовков, управляющий символ попадает в строку 1С.
В оперативной памяти платформы 1С строки хранятся в кодировке Unicode, поэтому любые присвоения и операции склейки строк выполняются без сбоев. Однако при вызове метода Записать() платформа выполняет внутреннюю сериализацию данных (в частности, при передаче между клиентом и сервером, формировании XML-пакетов регистрации изменений или записи в структуры СУБД).
Согласно стандарту XML 1.0, управляющие символы диапазона кодов от 0 до 31 (за исключением символов с кодами 9 — табуляция, 10 — перевод строки, 13 — возврат каретки) строго запрещены к использованию даже в виде сущностей (). При попытке сериализовать строку с такими символами платформа 1С прерывает операцию с критической ошибкой.
Платформа 1С предоставляет встроенную функцию глобального контекста НайтиНедопустимыеСимволыXML(). С ее помощью можно найти позицию первого некорректного символа и обработать строку — удалить невалидные знаки либо заменить их на читаемое представление.
Рассмотрим пример функции, которая заменяет все невалидные символы на их экранированные строковые коды:
Функция ЗаменитьНедопустимыеСимволы(Знач ИсходныйТекст) Экспорт
Позиция = 1;
Пока Истина Цикл
Позиция = НайтиНедопустимыеСимволыXML(ИсходныйТекст, Позиция);
Если Позиция = 0 Тогда
Прервать;
КонецЕсли;
Код = КодСимвола(Сред(ИсходныйТекст, Позиция, 1));
СпецСимвол = "[" + Формат(Код, "ЧГ=0") + "]";
ИсходныйТекст = Сред(ИсходныйТекст, 1, Позиция - 1) + СпецСимвол + Сред(ИсходныйТекст, Позиция + 1);
Позиция = Позиция + СтрДлина(СпецСимвол);
КонецЦикла;
Возврат ИсходныйТекст;
КонецФункции
Разберем, как работает этот алгоритм:
НайтиНедопустимыеСимволыXML ищет запрещенный символ, начиная с указанной позиции. Если такой символ не найден, возвращается 0.КодСимвола().[2]) и смещаем указатель поиска дальше по строке.Если конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), писать собственный цикл обработки не требуется. В общем модуле СтроковыеФункцииКлиентСервер предусмотрен готовый оптимизированный метод.
Посмотрим на пример вызова:
ОчищенныйТекст = СтроковыеФункцииКлиентСервер.УдалитьНедопустимыеСимволыXML(СтрокаДанных);
СтрокаТЧ.Результаты = ОчищенныйТекст;
Этот метод за один проход удаляет все управляющие символы, нарушающие спецификацию XML, что гарантирует успешную запись объекта без потери полезных данных.
Наиболее архитектурно правильный подход — очищать управляющие символы непосредственно в модуле интеграции при чтении файла или сетевого потока. Если протокол обмена использует символы начала и конца кадра (STX — 0x02, ETX — 0x03, EOT — 0x04, ACK — 0x06), их необходимо отсекать регулярными выражениями или строковыми заменами до передачи в табличные части документов:
// Удаляем известные символы кадрирования
СимволSTX = Символ(2);
СимволETX = Символ(3);
СимволEOT = Символ(4);
ЧистаяСтрока = СтрЗаменить(СтрЗаменить(СтрЗаменить(СыраяСтрока, СимволSTX, ""), СимволETX, ""), СимволEOT, "");
Если для целей аудита или технической диагностики требуется сохранить точный «сырой» ответ анализатора вместе со всеми непечатными байтами, обычный строковый реквизит использовать нельзя. В таком случае применяют один из двух вариантов:
ДвоичныеДанные и помещаются в ХранилищеЗначения. Данный тип не подвергается строгой XML-валидации символов при записи.Base64Строка() и Base64Значение(). Строка в формате Base64 состоит исключительно из латинских букв, цифр и знаков +, /, =, что полностью исключает конфликты с XML-сериализацией.