Как в 1С сформировать XML-файл без атрибута encoding в заголовке

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

При интеграции с внешними системами разработчики нередко сталкиваются с ситуацией, когда принимающий сервис требует строго определенный формат заголовка XML. Например, принимающий парсер ожидает ровно <?xml version="1.0"?> и выдает ошибку при наличии атрибута encoding="UTF-8". Однако при стандартном экспорте в файл платформа 1С автоматически дописывает кодировку в заголовок. Разберем, почему так происходит и какими способами можно получить требуемый результат.

Проанализируем сначала поведение платформы. Когда мы создаем объект ЗаписьXML и связываем его напрямую с диском через метод ОткрытьФайл(), платформа обязана ассоциировать поток с физической кодировкой. По умолчанию используется UTF-8. Поэтому последующий вызов метода ЗаписатьОбъявлениеXML() всегда выводит атрибут кодировки. Передача пустой кодировки в ПараметрыЗаписиXML не помогает — платформа все равно применит значение по умолчанию. Также нельзя обойти ограничение вызовом метода ЗаписатьИнструкциюОбработки("xml", "version=""1.0"""): имя «xml» зарезервировано стандартом, и платформа выбросит исключение.

Тем не менее, решить задачу можно несколькими надежными путями в зависимости от объема выгружаемых данных.

Способ 1. Формирование XML в строке с последующей записью через ЗаписьТекста

Рассмотрим основной способ, который подходит для небольших и средних документов. Если объект ЗаписьXML инициализируется в оперативной памяти через метод УстановитьСтроку(), кодировка выходного потока не фиксируется заранее. В этом случае метод ЗаписатьОбъявлениеXML() генерирует чистый заголовок <?xml version="1.0"?> без указания кодировки.

После завершения формирования структуры мы получаем готовую строку и сохраняем ее на диск при помощи объекта ЗаписьТекста. Посмотрим на пример реализации:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();

// Формируем заголовок без атрибута encoding
ЗаписьXML.ЗаписатьОбъявлениеXML();

// Заполняем тело документа
ЗаписьXML.ЗаписатьНачалоЭлемента("Данные");
ЗаписьXML.ЗаписатьАтрибут("ВерсияФормата", "1.0");
ЗаписьXML.ЗаписатьНачалоЭлемента("Элемент");
ЗаписьXML.ЗаписатьТекст("Тестовое значение");
ЗаписьXML.ЗаписатьКонецЭлемента(); // Элемент
ЗаписьXML.ЗаписатьКонецЭлемента(); // Данные

ТекстXML = ЗаписьXML.Закрыть();

// Сохраняем полученную строку в файл
ЗаписьТекста = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8);
ЗаписьТекста.ЗаписатьСтроку(ТекстXML);
ЗаписьТекста.Закрыть();

Разберем по шагам, что здесь происходит:

  1. Метод УстановитьСтроку() переводит объект ЗаписьXML в режим записи в оперативную память.
  2. Метод ЗаписатьОбъявлениеXML() добавляет строку <?xml version="1.0"?>, не добавляя кодировку, так как строковый буфер платформы не привязан к конкретному файловому формату.
  3. Метод Закрыть() возвращает сформированный XML в виде обычной строки 1С.
  4. Объект ЗаписьТекста записывает готовый текст в файл на диске с необходимой кодировкой без изменения содержимого самого XML.

Способ 2. Отказ от вызова ЗаписатьОбъявлениеXML

Выясним еще одну особенность стандарта XML. Согласно официальной спецификации XML 1.0, объявление <?xml ...?> в самом начале документа не является строго обязательным. Если принимающий парсер не настаивает на наличии самого тега объявления, а спотыкается именно о неизвестный или нестандартный атрибут encoding, самым простым и производительным решением будет отказ от вызова ЗаписатьОбъявлениеXML().

Посмотрим на пример формирования напрямую в файл:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу);

// Метод ЗаписатьОбъявлениеXML() намеренно не вызывается
ЗаписьXML.ЗаписатьНачалоЭлемента("Данные");
ЗаписьXML.ЗаписатьТекст("Пример без пролога");
ЗаписьXML.ЗаписатьКонецЭлемента();

ЗаписьXML.Закрыть();

В этом случае файл сразу начинается с корневого элемента <Данные>. Подавляющее большинство корректных XML-парсеров по умолчанию интерпретируют документ в кодировке UTF-8.

Способ 3. Потоковая постобработка для файлов большого объема

Если файл занимает десятки или сотни мегабайт, формирование через УстановитьСтроку() приведет к ошибке нехватки оперативной памяти. Если при этом заголовок <?xml version="1.0"?> строго обязателен, применим потоковую замену первой строки через связку ЧтениеТекста и ЗаписьТекста.

Разберем этот алгоритм:

  1. Штатно формируем XML на диск через ЗаписьXML.ОткрытьФайл(), вызвав ЗаписатьОбъявлениеXML(). Файл запишется быстро и без расхода оперативной памяти, но в первой строке будет присутствовать encoding="UTF-8".
  2. Открываем исходный файл на чтение через ЧтениеТекста, а новый временный файл на запись через ЗаписьТекста.
  3. Считываем первую строку и заменяем в ней заголовок на нужный вид без кодировки.
  4. Все последующие строки переносим в цикле без изменений.
  5. Заменяем исходный файл временным через ПереместитьФайл().

Посмотрим, как выглядит потоковая замена на практике:


// Предположим, что файл ИсходныйФайл.xml уже сформирован через ЗаписьXML
ФайлИсточника = "C:\Temp\Export.xml";
ФайлРезультата = "C:\Temp\Export_Ready.xml";

Чтение = Новый ЧтениеТекста(ФайлИсточника, КодировкаТекста.UTF8);
Запись = Новый ЗаписьТекста(ФайлРезультата, КодировкаТекста.UTF8);

ПерваяСтрока = Истина;
Строка = Чтение.ПрочитатьСтроку();

Пока Строка <> Неопределено Цикл
    Если ПерваяСтрока Тогда
        // Заменяем заголовок с кодировкой на чистый вариант
        Если СтрНачинаетсяС(Строка, "<?xml") Тогда
            Строка = "<?xml version=""1.0""?>";
        КонецЕсли;
        ПерваяСтрока = Ложь;
    КонецЕсли;
    
    Запись.ЗаписатьСтроку(Строка);
    Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;

Чтение.Закрыть();
Запись.Закрыть();

УдалитьФайлы(ФайлИсточника);
ПереместитьФайл(ФайлРезультата, ФайлИсточника);

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

← На главную