При интеграции 1С со сторонними информационными системами разработчики часто сталкиваются с жесткими требованиями к формату файлов обмена. Одной из типичных проблем является появление нежелательных кавычек в результирующем XML-файле. Например, вместо ожидаемого значения <olcode>99997</olcode> принимающая система получает строку вида <olcode>="99997"</olcode> или атрибуты, которые жестко заключены в кавычки, что вызывает ошибки при парсинге на стороне контрагента.
В этой статье мы подробно разберем, почему возникают кавычки при выгрузке в XML, как стандартными средствами платформы сформировать корректную структуру данных и что делать, если принимающая сторона требует нестандартного форматирования.
Для начала проанализируем, как устроен формат XML и почему платформа 1С записывает данные в кавычках. В XML-документе данные могут храниться двумя способами:
<olcode>99997</olcode>. В этом случае кавычки не используются.<obj olcode="99997" />.Согласно строгому международному стандарту W3C XML, значения любых атрибутов обязательно должны быть заключены в кавычки (одинарные или двойные). Платформа 1С строго следует спецификации XML. Если в коде используется метод ЗаписатьАтрибут(), платформа автоматически и принудительно обернет значение в кавычки. Обойти это поведение при записи атрибутов штатными средствами невозможно.
В исходном коде автора темы использовалась следующая конструкция:
ЗаписьXML.ЗаписатьАтрибут("olcode", СокрЛП(СтрЗаменить(СтрокаТЗ.КодТРТ, Символы.НПП, "")));
Именно использование метода ЗаписатьАтрибут() приводило к тому, что в итоговом файле данные выгружались с кавычками. Если принимающая сторона требует передавать «чистые» значения без кавычек, это означает, что данные должны быть записаны как текстовое содержимое элементов, а не как их атрибуты.
---
Рассмотрим, как правильно переписать код формирования XML, чтобы избавиться от кавычек. Вместо записи атрибутов мы будем создавать полноценные вложенные теги (элементы) и записывать в них текст с помощью метода ЗаписатьТекст().
Посмотрим на пример преобразования кода:
Было (с кавычками):
ЗаписьXML.ЗаписатьАтрибут("olcode", СокрЛП(СтрокаТЗ.КодТРТ));
Стало (без кавычек):
ЗаписьXML.ЗаписатьНачалоЭлемента("olcode");
ЗаписьXML.ЗаписатьТекст(СокрЛП(СтрокаТЗ.КодТРТ));
ЗаписьXML.ЗаписатьКонецЭлемента();
Давайте полностью переработаем исходный алгоритм выгрузки из сообщения автора с учетом этого правила. Мы заменим выгрузку всех ключевых реквизитов с атрибутной модели на элементную:
ТЗДолги = ПолучитьХОДляВыгрузки(Производитель, ГруппаХО, КонецПериода);
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(КаталогСервера + "pos.xml", Новый ПараметрыЗаписиXML("windows-1251", , Ложь, Ложь));
ЗаписьXML.ЗаписатьОбъявлениеXML(); // Записываем объявление XML
ЗаписьXML.ЗаписатьНачалоЭлемента("objects"); // Начало корневого элемента
Для Каждого СтрокаТЗ Из ТЗДолги Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента("obj"); // Начало элемента объекта
// Выгружаем "custid" как элемент
ЗаписьXML.ЗаписатьНачалоЭлемента("custid");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТЗ.КодЗавода));
ЗаписьXML.ЗаписатьКонецЭлемента();
// Выгружаем "posid" как элемент с проверкой на заполнение
ЗаписьXML.ЗаписатьНачалоЭлемента("posid");
Если ЗначениеЗаполнено(СтрокаТЗ.УИДХО) Тогда
ЗаписьXML.ЗаписатьТекст(XMLСтрока(Число(СокрЛП(СтрокаТЗ.УИДХО))));
Else
ЗаписьXML.ЗаписатьТекст(XMLСтрока(0));
КонецЕсли;
ЗаписьXML.ЗаписатьКонецЭлемента();
// Выгружаем "olcode" без кавычек
ЗаписьXML.ЗаписатьНачалоЭлемента("olcode");
ЗаписьXML.ЗаписатьТекст(СокрЛП(СтрЗаменить(СтрокаТЗ.КодТРТ, Символы.НПП, "")));
ЗаписьXML.ЗаписатьКонецЭлемента();
// Выгружаем техническое состояние
ЗаписьXML.ЗаписатьНачалоЭлемента("technicalcondition");
ЗаписьXML.ЗаписатьТекст(СокрЛП(СтрокаТЗ.СостояниеХО));
ЗаписьXML.ЗаписатьКонецЭлемента();
// Пустой элемент reasonrepair
ЗаписьXML.ЗаписатьНачалоЭлемента("reasonrepair");
ЗаписьXML.ЗаписатьКонецЭлемента();
// Номер документа
ЗаписьXML.ЗаписатьНачалоЭлемента("tsconno");
ЗаписьXML.ЗаписатьТекст(СокрЛП(СтрокаТЗ.НомерДок));
ЗаписьXML.ЗаписатьКонецЭлемента();
// Текущая дата в формате XML (ГГГГ-ММ-ДД)
ЗаписьXML.ЗаписатьНачалоЭлемента("dtlm");
ЗаписьXML.ЗаписатьТекст(Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd"));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьКонецЭлемента(); // Конец элемента obj
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // Конец элемента objects
ЗаписьXML.Закрыть();
Такой подход гарантирует создание валидного XML-файла, где все текстовые данные будут выгружены без кавычек.
---
ЗаписатьБезОбработки() для обхода стандартовИногда возникают ситуации, когда принимающая сторона использует некорректно написанный ("самописный") парсер, который требует нарушить стандарты XML. Например, если от вас требуют выгрузить атрибуты вообще без кавычек (что с точки зрения спецификации XML является ошибкой): <obj custid=99997>.
Стандартный метод ЗаписатьАтрибут() всегда добавит кавычки. Чтобы обойти это жесткое ограничение платформы, мы можем воспользоваться методом ЗаписатьБезОбработки() объекта ЗаписьXML.
Проанализируем пример использования этого метода:
// Записываем тег и атрибут без кавычек напрямую в поток
СтрокаАтрибутов = "<obj custid=" + XMLСтрока(СтрокаТЗ.КодЗавода) + ">";
ЗаписьXML.ЗаписатьБезОбработки(СтрокаАтрибутов);
Важное предупреждение: При использовании метода ЗаписатьБезОбработки() вся ответственность за валидность структуры XML-документа, экранирование спецсимволов (таких как &, <, >) и кодировку полностью ложится на плечи разработчика. Платформа 1С в данном случае не выполняет никаких проверок.
---
Если структура итогового XML-документа должна содержать спецсимволы в особом формате, или если вам нужно программно убрать определенные кавычки из уже готового файла, можно прибегнуть к пост-обработке файла как обычного текста.
Разберем по шагам, как это реализовать:
ЗаписьXML) и закрываем его, чтобы сохранить данные на диск.ТекстовыйДокумент.СтрЗаменить().Рассмотрим пример кода, который заменяет автоматически экранированные кавычки (сущности ") обратно на стандартные символы кавычек, если принимающий парсер не умеет их декодировать:
ПутьКФайлу = КаталогСервера + "pos.xml";
// Читаем сформированный XML как текстовый файл
ТекстДокумента = Новый ТекстовыйДокумент;
ТекстДокумента.Прочитать(ПутьКФайлу, КодировкаТекста.UTF8);
// Получаем текст файла
ИсходныйТекст = ТекстДокумента.ПолучитьТекст();
// Заменяем экранированные сущности на физические кавычки
НовыйТекст = СтрЗаменить(ИсходныйТекст, """, """");
// Перезаписываем файл с новыми параметрами
ТекстДокумента.УстановитьТекст(НовыйТекст);
ТекстДокумента.Записать(ПутьКФайлу, КодировкаТекста.UTF8);
В случаях, когда структура файла предельно простая, разработчики часто вообще отказываются от использования специализированного объекта ЗаписьXML и собирают строку XML вручную, записывая её в файл через ЗаписьТекста. Это дает абсолютный контроль над каждым байтом и символом в итоговом документе.
---
XMLСтрока()Обратите внимание на то, как преобразуются типы данных при выгрузке. В исходном коде автора для текстовых полей использовалась функция СокрЛП(), а для чисел пытались применить явное приведение типов. При работе с XML крайне важно использовать встроенную функцию XMLСтрока().
Выясним, почему это критично:
Строка() или СокрЛП() для числа), результат будет зависеть от региональных настроек операционной системы сервера или пользователя. Например, число 150000 может выгрузиться как "150 000" (с неразрывным пробелом в качестве разделителя групп разрядов). На принимающей стороне это гарантированно вызовет ошибку парсинга.XMLСтрока() гарантирует приведение значений к каноническому стандарту XML. Числа всегда будут выгружаться без пробелов-разделителей групп и с точкой в качестве разделителя дробной части (например, 150000 или 1500.55).XMLСтрока() автоматически приводятся к международному стандарту ISO 8601 (формат YYYY-MM-DDThh:mm:ss), что исключает проблемы с интерпретацией дат принимающими системами.Применяйте рассмотренные методики в зависимости от требований принимающей стороны, и ваши файлы обмена всегда будут обрабатываться корректно и без ошибок!