При интеграции систем 1С с внешними сервисами, государственными порталами (такими как СФР, ФТС, МЧД) или банковскими API разработчики часто сталкиваются с жесткими требованиями к формату XML-файлов. Одной из распространенных проблем является обработка символа апострофа (одинарной кавычки '). В этой статье мы подробно разберем, почему стандартные механизмы 1С отказываются заменять апостроф на сущность ', почему ручная замена приводит к ошибкам и как правильно решить эту задачу без искажения исходных данных.
Для начала проанализируем поведение самой платформы 1С. Многие разработчики ожидают, что при вызове методов ЗаписьXML.ЗаписатьТекст() или функции XMLСтрока() все спецсимволы будут автоматически преобразованы в соответствующие XML-сущности. Однако с апострофом этого не происходит. Почему?
Поведение платформы 1С строго соответствует официальному стандарту консорциума W3C для XML. Согласно спецификации, в XML существует 5 предопределенных сущностей:
< для знака «меньше» (<)> для знака «больше» (>)& для амперсанда (&)" для двойной кавычки (")' для апострофа (')Согласно стандарту, обязательному экранированию в текстовых узлах (внутри тегов) подлежат только символы < и &. Символы кавычек и апострофов обязаны экранироваться только внутри значений атрибутов, и то лишь в тех случаях, когда они совпадают с символом-ограничителем самого атрибута (например, если значение атрибута обернуто в одинарные кавычки: name='O'Connor').
Внутри обычного текстового элемента запись вида <name>Об'єм</name> является абсолютно валидной. Платформа 1С оптимизирует размер файла и быстродействие, поэтому не выполняет избыточную замену символа ' на '. Но как быть, если принимающая сторона (парсер сторонней системы) написана некорректно или использует жесткие регулярные выражения и требует обязательного наличия '?
Рассмотрим типичные ошибки, которые совершают разработчики при попытке обойти это ограничение:
СтрЗаменить(ТекстТега, "'", "’"). С точки зрения прохождения валидации XML-схемы это сработает. Однако этот способ искажает исходные данные. Для базы данных «Об'єм» и «Об’єм» — разные строки. Если принимающая система производит поиск по ключевым полям (например, названия организаций, фамилии или адреса), автоматическое сопоставление данных завершится ошибкой.Символ(37) приведет к тому, что вместо апострофа в XML запишется знак процента % (например, «Об%єм»), так как код 37 в таблице ASCII соответствует именно этому символу. Прямой апостроф имеет код 39 (Символ(39)), но его простая подстановка в текстовую переменную не изменит поведения ЗаписьXML.СтрЗаменить(ТекстТега, "'", "'") перед записью в XML, то на выходе получим некорректную строку вида &apos;. Это происходит потому, что объект ЗаписьXML видит символ амперсанда & во вновь созданной строке и послушно экранирует его, превращая в &.Самый надежный и корректный способ гарантированно получить сущность ' (или её числовой эквивалент ') в финальном файле — это выполнить замену символов после того, как XML-документ был полностью сгенерирован платформой 1С. Таким образом, мы обойдем встроенный парсер ЗаписьXML и избежим двойного экранирования амперсанда.
Разберем этот алгоритм по шагам:
СтрЗаменить для принудительной замены одиночной кавычки на требуемую сущность.ЗаписьТекста с указанием нужной кодировки (обычно UTF-8).Посмотрим на пример реализации данного метода:
// Шаг 1. Инициализируем запись XML в память
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьXML.ЗаписатьОбъявлениеXML();
// Шаг 2. Формируем структуру XML-документа
ЗаписьXML.ЗаписатьНачалоЭлемента("Данные");
ЗаписьXML.ЗаписатьНачалоЭлемента("Параметр");
ТекстТега = "Об'єм"; // Исходная строка с апострофом
ЗаписьXML.ЗаписатьТекст(ТекстТега);
ЗаписьXML.ЗаписатьКонецЭлемента(); // Параметр
ЗаписьXML.ЗаписатьКонецЭлемента(); // Данные
// Шаг 3. Получаем XML-текст из объекта записи
ТекстXML = ЗаписьXML.Закрыть();
// Шаг 4. Выполняем замену апострофа на сущность ' (или ее числовой код ')
// Некоторые старые парсеры лучше поддерживают числовой код '
ТекстXML = СтрЗаменить(ТекстXML, "'", "'");
// Шаг 5. Записываем готовый текст в физический файл на диске
ЗаписьТекста = Новый ЗаписьТекста;
ЗаписьТекста.Открыть(ПутьКФайлу, КодировкаТекста.UTF8);
ЗаписьТекста.Записать(ТекстXML);
ЗаписьТекста.Закрыть();
Этот метод гарантирует, что структура XML останется абсолютно валидной, а все апострофы будут корректно экранированы в соответствии с жесткими требованиями внешней системы.
Если принимающая сторона способна корректно обрабатывать спецификацию XML в полном объеме, можно использовать механизм секций CDATA. Данные, помещенные в такую секцию, не анализируются XML-процессором на наличие спецсимволов. Это позволяет передавать тексты, содержащие апострофы, кавычки, амперсанды и знаки сравнения, в их первозданном виде.
Проанализируем пример кода с использованием метода ЗаписатьСекциюCDATA:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу, "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Данные");
ЗаписьXML.ЗаписатьНачалоЭлемента("Параметр");
ТекстТега = "Об'єм";
// Записываем значение внутри блока CDATA
ЗаписьXML.ЗаписатьСекциюCDATA(ТекстТега);
ЗаписьXML.ЗаписатьКонецЭлемента(); // Параметр
ЗаписьXML.ЗаписатьКонецЭлемента(); // Данные
ЗаписьXML.Закрыть();
В результате работы этого кода в XML-файле сформируется следующая структура:
<Параметр><![CDATA[Об'єм]]></Параметр>
Этот подход избавляет от необходимости ручной замены строк и обеспечивает стопроцентную сохранность передаваемых данных. Однако перед его внедрением обязательно убедитесь, что веб-сервис или парсер на стороне получателя умеет корректно извлекать данные из секций CDATA.