При разработке функционала для работы с электронной почтой или при отображении сложных веб-страниц внутри 1С программисты часто сталкиваются с ситуацией, когда необходимо передать полученный на сервере HTML-текст на сторону клиента для его отображения в ПолеHTMLДокумента. Казалось бы, задача тривиальная — передать строку через структуру или параметр функции. Однако на практике система нередко выдает загадочную ошибку: «Ошибка разбора XML: -[1,1]».
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем механизмы сериализации данных в 1С и рассмотрим несколько надежных способов передачи «проблемных» строк, которые гарантированно избавят вас от ошибок разбора.
Прежде чем переходить к коду, давайте поймем физику процесса. В платформе 1С:Предприятие 8 передача данных между сервером и тонким (или веб-) клиентом происходит не напрямую. Все данные, которые мы возвращаем из серверной функции, упаковываются во внутренний формат обмена, основанный на XML.
Проблема заключается в том, что стандарт XML 1.0 накладывает жесткие ограничения на используемые символы. В частности, запрещены так называемые «непечатные» управляющие символы ASCII в диапазоне 0x00–0x1F (за исключением символов табуляции, перевода строки и возврата каретки). Поскольку HTML-текст писем часто формируется сторонними почтовыми клиентами или парсерами, в нем могут содержаться невидимые спецсимволы, например, нулевой байт \0. Как только такой символ попадает в поток сериализации, XML-парсер 1С на приемной стороне (на клиенте) «спотыкается», выдавая ошибку в первой позиции первой строки — -[1,1].
Рассмотрим наиболее простой и эффективный метод, который часто применяется в типовых конфигурациях от фирмы 1С. Суть метода заключается в том, что мы превращаем строку в ДвоичныеДанные. Двоичные данные сериализуются платформой без попыток анализа их содержимого, что позволяет безопасно «протащить» любые запрещенные символы.
Разберем по шагам реализацию этого метода. На стороне сервера мы преобразуем полученный HTML-текст в поток байтов:
&НаСервере
Функция ПолучитьТелоПисьмаНаСервере()
// Предположим, мы получили HTML-текст из почтового сообщения
ТекстHTML = "Пример текста с возможной 'запрещенкой'...";
// Преобразуем строку в двоичные данные, используя кодировку UTF-8
ДанныеДляПередачи = ПолучитьДвоичныеДанныеИзСтроки(ТекстHTML, КодировкаТекста.UTF8, Ложь);
Возврат ДанныеДляПередачи;
КонецФункции
Теперь на клиенте нам нужно выполнить обратную операцию, чтобы передать текст в реквизит, связанный с полем HTML-документа:
&НаКлиенте
Процедура ПоказатьПисьмо()
ДвоичныеДанные = ПолучитьТелоПисьмаНаСервере();
// Восстанавливаем строку из двоичных данных
Объект.ТекстHTMLПоле = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8);
КонецПроцедуры
Важный нюанс: Обратите внимание на использование третьего параметра в функции ПолучитьДвоичныеДанныеИзСтроки — это флаг добавления BOM (Byte Order Mark). Мы установили его в Ложь, чтобы избежать появления лишних артефактов в начале HTML-кода, которые могут некорректно интерпретироваться движком отображения (особенно в Internet Explorer).
Если объем передаваемого HTML-текста велик (например, длинная переписка с внедренными картинками в формате Base64), передача через параметры функции может негативно сказаться на производительности. В этом случае проанализируем возможность использования Временного хранилища.
Этот механизм работает в обход стандартной XML-сериализации параметров вызова процедуры, что делает его не только безопасным с точки зрения символов, но и более быстрым. Рассмотрим алгоритм:
Посмотрим на пример кода:
&НаСервере
Функция ПоместитьHTMLВоХранилище()
ТекстHTML = "Большой объем данных...";
// Помещаем во временное хранилище, привязывая к уникальному идентификатору формы
АдресХранилища = ПоместитьВоВременноеХранилище(ТекстHTML, ЭтаФорма.УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
&НаКлиенте
Процедура ЗагрузитьHTML()
Адрес = ПоместитьHTMLВоХранилище();
// Извлекаем данные на клиенте
ТекстДляПоля = ПолучитьИзВременногоХранилища(Адрес);
Объект.ТекстHTMLПоле = ТекстДляПоля;
КонецПроцедуры
Иногда нам принципиально важно передать именно тип Строка (например, если это требуется интерфейсом взаимодействия внешних систем). В такой ситуации мы можем применить фильтрацию. Разберем ситуацию: нам нужно удалить все символы с кодами меньше 32, которые не являются стандартными переносами строк.
Проанализируем пример функции очистки, которую можно вызвать перед отправкой данных на клиент:
&НаСервере
Функция ОчиститьСтрокуДляXML(Знач ИсходнаяСтрока)
// Оставляем только допустимые символы: Tab (9), LF (10), CR (13) и все от Space (32)
Результат = "";
Для Позиция = 1 По СтрДлина(ИсходнаяСтрока) Цикл
КодСимвола = КодСимвола(ИсходнаяСтрока, Позиция);
Если КодСимвола >= 32 Или КодСимвола = 9 Или КодСимвола = 10 Или КодСимвола = 13 Тогда
Результат = Результат + Сред(ИсходнаяСтрока, Позиция, 1);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Совет: Если строка очень длинная, перебор символов в цикле 1С может работать медленно. В таких случаях эффективнее использовать регулярные выражения через COM-объект VBScript.RegExp, если сервер работает под управлением ОС Windows.
При работе с ПолемHTMLДокумента важно учитывать еще несколько моментов, которые помогут сделать работу приложения более стабильной:
ХранилищаЗначения с включенным сжатием. На сервере: Хранилище = Новый ХранилищеЗначения(ТекстHTML, Новый СжатиеДанных(9)). Это существенно сократит объем трафика между сервером и клиентом.Элементы.ВашеПолеHTML.Документ и изменить конкретный узел DOM. Это избавит пользователя от «мерцания» страницы при обновлении.Подводя итог, можно сказать, что наиболее универсальным и современным способом передачи «грязного» HTML является преобразование в двоичные данные. Это решение требует минимум кода, работает максимально быстро и полностью нивелирует риск возникновения ошибок разбора XML в тонком клиенте.