Обмен данными через файлы формата XML — одна из самых частых задач при интеграции 1С с внешними системами и веб-сервисами. Однако при реализации загрузки разработчики нередко сталкиваются с целым комплексом проблем: синтаксическими ошибками, ограничением доступа к файлам на сервере в управляемом приложении, дублированием элементов в справочниках и ошибочным обращением к объектам базы данных на стороне клиента.
Разберем по шагам, как правильно прочитать файл XML, распарсить его содержимое, создать или обновить элементы справочника без дублей, а также выгрузить данные обратно.
Рассмотрим типичные синтаксические ошибки, с которых часто начинается разработка механизмов импорта XML.
Первая ошибка возникает на этапе объявления процедуры, когда вместо имени параметра передается конкретная строка с путем к файлу:
// ОШИБКА: Ожидается имя формального параметра
Процедура ПрочестьИз("D:\Work\User\Desktop\postav.xml")
// Код процедуры
КонецПроцедуры
В заголовке процедуры 1С должны указываться только имена переменных-параметров. Корректное объявление выглядит следующим образом:
Процедура ПрочестьИз(ПутьКФайлу)
// Внутри процедуры работаем с переменной ПутьКФайлу
КонецПроцедуры
Вторая распространенная ошибка — обращение к некорректным или необъявленным переменным при обходе узлов XML через DOM (Document Object Model). Если цикл объявлен по переменной Элемент0, а внутри обращения идут к несуществующему объекту Contr, платформа выдаст ошибку «Переменная не определена»:
// ОШИБКА: Переменная Contr не определена
Для Каждого Элемент0 Из Документ.ЭлементДокумента.ДочерниеУзлы Цикл
Если Элемент0.ИмяУзла = "Contr" Тогда
КПП = Contr.Атрибуты.ПолучитьИменованныйЭлемент("KPP"); // Ошибка здесь!
КонецЕсли;
КонецЦикла;
Для обращения к текущему узлу нужно использовать имя переменной цикла, то есть Элемент0.
Платформа 1С предлагает три основных инструмента для работы с XML-документами:
ЧтениеXML): Считывает файл строго по очереди узел за узлом. Отличается минимальным потреблением оперативной памяти, поэтому идеально подходит для файлов огромного объема (десятки и сотни мегабайт).ПостроительDOM): Полностью загружает структуру XML в оперативную память, создавая дерево объектов. Позволяет перемещаться по узлам в любых направлениях и выполнять поиск через XPath, но требователен к ресурсам.ФабрикаXDTO.ПрочитатьXML): Преобразует файл XML в высокоуровневые объекты XDTO. Это наиболее удобный и лаконичный способ для чтения структурированных файлов небольшого и среднего размера.Проанализируем пример чтения файла поставщиков с помощью ФабрикаXDTO:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("D:\Work\User\Desktop\postav.xml");
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
Для Каждого Contr Из ОбъектXDTO.RegContr.Contr Цикл
Сообщить("ИНН: " + Contr.INN);
Сообщить("КПП: " + Contr.KPP);
Сообщить("Наименование: " + Contr.SNAME);
КонецЦикла;
ЧтениеXML.Закрыть();
В управляемом приложении код разделен на клиентский и серверный контексты. Диалог выбора файла ДиалогВыбораФайла доступен только на клиенте (директива &НаКлиенте), однако сервер 1С не имеет прямого доступа к файловой системе компьютера пользователя.
Если сервер и клиент находятся на разных компьютерах, вызов ЧтениеXML.ОткрытьФайл(ПутьКФайлу) на сервере завершится ошибкой, так как по указанному пути на сервере файла не окажется.
Рассмотрим правильную организацию интерактивного выбора и передачи файла с клиента на сервер:
&НаКлиенте
Процедура ВыбратьИПрочитатьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы XML (*.xml)|*.xml";
Диалог.МножественныйВыбор = Ложь;
Если Диалог.Выбрать() Тогда
// Получаем выбранный путь на клиенте
ПутьКФайлу = Диалог.ВыбранныеФайлы[0];
// Читаем содержимое файла в строку на клиенте
ТекстXML = Новый ТекстовыйДокумент;
ТекстXML.Прочитать(ПутьКФайлу);
СодержимоеXML = ТекстXML.ПолучитьТекст();
// Передаем строку на сервер
ОбработатьXMLНаСервере(СодержимоеXML);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ОбработатьXMLНаСервере(СодержимоеXML)
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СодержимоеXML);
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Дальнейшая обработка объектов XDTO на сервере...
ЧтениеXML.Закрыть();
КонецПроцедуры
Метод ЧтениеXML.УстановитьСтроку() позволяет передавать текст XML-файла непосредственно в память без необходимости создавать временные файлы на стороне сервера.
Выясним, как правильно записывать загружаемые данные в справочник Поставщики, избегая дублирования и обеспечивая обновление существующих записей.
Важно помнить разницу между понятиями Ссылка и Объект:
НайтиПоРеквизиту или НайтиПоНаименованию) возвращают СправочникСсылка. Через ссылку нельзя изменять реквизиты элемента..ПолучитьОбъект() и работать со СправочникОбъект.Проанализируем алгоритм:
ИНН).Справочники.Поставщики.СоздатьЭлемент()..ПолучитьОбъект() для дозаполнения недостающих реквизитов.
&НаСервере
Процедура ЗагрузитьПоставщиковИзXDTO(ОбъектXDTO, ГруппаРодитель)
Для Каждого Contr Из ОбъектXDTO.RegContr.Contr Цикл
// Пропускаем записи без ИНН
Если НЕ ЗначениеЗаполнено(Contr.INN) Тогда
Продолжить;
КонецЕсли;
// Ищем существующего поставщика по ИНН
СсылкаНайденного = Справочники.Поставщики.НайтиПоРеквизиту("ИНН", Contr.INN);
Если СсылкаНайденного.Пустая() Тогда
// Создаем новый элемент
ОбъектПоставщик = Справочники.Поставщики.СоздатьЭлемент();
ОбъектПоставщик.Наименование = Contr.SNAME;
ОбъектПоставщик.ИНН = Contr.INN;
ОбъектПоставщик.КПП = Contr.KPP;
Если ЗначениеЗаполнено(ГруппаРодитель) Тогда
ОбъектПоставщик.Родитель = ГруппаРодитель;
КонецЕсли;
ОбъектПоставщик.Записать();
Иначе
// Если элемент уже есть, обновляем пустые реквизиты
ОбъектПоставщик = СсылкаНайденного.ПолучитьОбъект();
Изменен = Ложь;
Если НЕ ЗначениеЗаполнено(ОбъектПоставщик.КПП) И ЗначениеЗаполнено(Contr.KPP) Тогда
ОбъектПоставщик.КПП = Contr.KPP;
Изменен = Истина;
КонецЕсли;
Если Изменен Тогда
ОбъектПоставщик.Записать();
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рассмотрим обратную задачу — выгрузку данных из справочника Поставщики в файл XML. Попытка вызвать менеджер справочника Справочники.Поставщики.Выбрать() на клиенте вызовет ошибку «Переменная не определена (Справочники)», так как прямые обращения к БД на клиенте запрещены.
Сформируем выгрузку на сервере с использованием объекта ЗаписьXML:
&НаКлиенте
Процедура ЭкспортироватьПоставщиков(Команда)
// Формируем текст XML на сервере
ТекстXML = ПолучитьТекстВыгрузкиXMLНаСервере();
// Сохраняем файл на клиенте
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
Диалог.Фильтр = "Файлы XML (*.xml)|*.xml";
Диалог.ПолноеИмяФайла = "postav_export.xml";
Если Диалог.Выбрать() Тогда
ТекстовыйДок = Новый ТекстовыйДокумент;
ТекстовыйДок.УстановитьТекст(ТекстXML);
ТекстовыйДок.Записать(Диалог.ПолноеИмяФайла);
Сообщить("Выгрузка успешно завершена!");
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПолучитьТекстВыгрузкиXMLНаСервере()
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("Root");
Запись.ЗаписатьНачалоЭлемента("RegContr");
Выборка = Справочники.Поставщики.Выбрать();
Пока Выборка.Следующий() Цикл
// Исключаем группы
Если Выборка.ЭтоГруппа Тогда
Продолжить;
КонецЕсли;
Запись.ЗаписатьНачалоЭлемента("Contr");
Запись.ЗаписатьАтрибут("SNAME", Строка(Выборка.Наименование));
Запись.ЗаписатьАтрибут("INN", Строка(Выборка.ИНН));
Запись.ЗаписатьАтрибут("KPP", Строка(Выборка.КПП));
Запись.ЗаписатьКонецЭлемента(); // Contr
КонецЦикла;
Запись.ЗаписатьКонецЭлемента(); // RegContr
Запись.ЗаписатьКонецЭлемента(); // Root
РезультатСтрокой = Запись.Закрыть();
Возврат РезультатСтрокой;
КонецФункции
Для создания надежных и быстрых алгоритмов обмена данными следует придерживаться следующих правил:
НачатьТранзакцию() / ЗафиксироватьТранзакцию(). Это ускорит загрузку в несколько раз.ИНН и КПП.НайтиПоРеквизиту() внутри цикла создают лишнюю нагрузку на СУБД. Эффективнее перед началом цикла загрузить существующие ИНН из справочника в объект Соответствие или ТаблицаЗначений в оперативной памяти и выполнять поиск по ним.