При разработке различных интеграций и обработок в среде 1С:Предприятие 8 часто возникает задача автоматизации ручного ввода. Чтобы пользователю не приходилось многократно заполнять одни и те же реквизиты на форме (например, при подготовке документов, заполнении настроек или импорте данных), отличным решением является загрузка этих параметров из внешнего файла XML. В этой статье мы подробно и по шагам разберем, как реализовать этот процесс на управляемых формах, какие подводные камни могут встретиться при клиент-серверном взаимодействии и какие программные механизмы платформы 1С подходят для этого лучше всего.
Прежде чем приступать непосредственно к разбору XML, необходимо решить ключевую задачу управляемого приложения — передать выбранный пользователем файл с его локального компьютера (клиента) на сервер 1С, где будет выполняться основной код парсинга. Напрямую использовать объект ЧтениеXML и открывать локальный путь к файлу на сервере нельзя, так как сервер «не видит» жесткий диск пользователя.
Проанализируем правильный алгоритм реализации этого процесса:
Рассмотрим пример реализации этой логики в модуле управляемой формы:
&НаКлиенте
Процедура ЗагрузитьXMLНажатие(Команда)
// Настраиваем диалог выбора файла
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы XML (*.xml)|*.xml";
Диалог.МножественныйВыбор = Ложь;
Диалог.Заголовок = "Выберите XML-файл для заполнения реквизитов";
Если Диалог.Выбрать() Тогда
// Подготавливаем описание файла для передачи на сервер
ОписаниеФайла = Новый ОписаниеПередаваемогоФайла(Диалог.ПолноеИмяФайла);
МассивФайлов = Новый Массив;
МассивФайлов.Добавить(ОписаниеФайла);
// Асинхронно помещаем файл во временное хранилище
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайловНаСервер(ОписаниеОповещения, МассивФайлов, , Ложь, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, ДополнительныеПараметры) Экспорт
Если Результат.Количество() > 0 И ЗначениеЗаполнено(Результат[0].Хранилище) Тогда
// Передаем полученный адрес временного хранилища на сервер
ЗаполнитьРеквизитыИзФайлаНаСервере(Результат[0].Хранилище);
ПоказатьПредупреждение(, "Реквизиты формы успешно заполнены из файла XML!");
КонецЕсли;
КонецПроцедуры
Теперь, когда клиентская часть подготовлена, мы можем перейти на сторону сервера и реализовать непосредственно чтение данных. Существует несколько способов разбора XML в 1С. Рассмотрим каждый из них подробно, чтобы вы могли выбрать наиболее подходящий под вашу задачу.
Этот способ является базовым и самым экономным с точки зрения потребления оперативной памяти. Объект ЧтениеXML работает по принципу SAX (Simple API for XML): он не загружает весь документ в память компьютера, а последовательно переходит от одного узла к другому (сверху вниз). Это критически важно, если размер обрабатываемого XML-файла исчисляется сотнями мегабайт или гигабайтами.
Выясним, как написать код последовательного чтения для заполнения реквизитов нашей обработки на сервере:
&НаСервере
Процедура ЗаполнитьРеквизитыИзФайлаНаСервере(АдресХранилища)
// Получаем двоичные данные из временного хранилища
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
// Создаем временный файл на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
// Инициализируем объект чтения XML
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяВременногоФайла);
// Переменная для хранения текущего обрабатываемого тега
ТекущееИмяТега = "";
Пока Чтение.Прочитать() Цикл
// Определяем тип узла
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ТекущееИмяТега = Чтение.ЛокальноеИмя;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
// Сопоставляем имена тегов из XML с реквизитами нашей обработки
Если ТекущееИмяТега = "Организация" Тогда
// Для ссылочных типов данных рекомендуется использовать поиск
НайденнаяОрганизация = Справочники.Организации.НайтиПоНаименованию(Чтение.Значение);
Если Не НайденнаяОрганизация.Пустая() Тогда
Объект.Организация = НайденнаяОрганизация;
КонецЕсли;
ИначеЕсли ТекущееИмяТега = "Контрагент" Тогда
НайденныйКонтрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", Чтение.Значение);
Если Не НайденныйКонтрагент.Пустая() Тогда
Объект.Контрагент = НайденныйКонтрагент;
КонецЕсли;
ИначеЕсли ТекущееИмяТега = "ДатаДокумента" Тогда
// Приводим текстовое представление даты к типу Дата в 1С
Объект.ДатаДокумента = XMLЗначение(Тип("Дата"), Чтение.Значение);
ИначеЕсли ТекущееИмяТега = "Сумма" Тогда
Объект.СуммаОперации = XMLЗначение(Тип("Число"), Чтение.Значение);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Обязательно закрываем файл
Чтение.Закрыть();
// Удаляем временный файл с диска сервера
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Запись в журнал регистрации при невозможности удаления
КонецПопытки;
КонецПроцедуры
Данный подход отлично работает, но если XML имеет сложную многоуровневую структуру со множеством вложенных тегов с одинаковыми именами, код с обилием конструкций Если... ИначеЕсли... быстро становится громоздким и трудночитаемым.
Когда файл XML имеет средний или небольшой размер (до нескольких десятков мегабайт), гораздо удобнее использовать объектную модель документа — DOM (Document Object Model). Платформа 1С считывает весь файл целиком и строит в оперативной памяти древовидную структуру. Главное преимущество DOM — возможность точечного позиционирования на нужных узлах с помощью технологии XPath без написания сложных и длинных циклов обхода.
Проанализируем пример использования DOM для быстрого извлечения конкретных данных:
&НаСервере
Процедура ЗаполнитьЧерезDOM(АдресХранилища)
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяВременногоФайла);
// Читаем документ в модель DOM
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(Чтение);
Чтение.Закрыть();
// Пример точечного поиска узла с помощью XPath-выражения
// Ищем тег "ИНН" внутри блока "Контрагент", расположенного в корневом узле "Данные"
РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath("/Данные/Контрагент/ИНН");
УзелИНН = РезультатXPath.ПолучитьСледующий();
Если УзелИНН <> Неопределено Тогда
ТекстИНН = УзелИНН.ТекстовоеСодержимое;
НайденныйКонтрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ТекстИНН);
Если Не НайденныйКонтрагент.Пустая() Тогда
Объект.Контрагент = НайденныйКонтрагент;
КонецЕсли;
КонецЕсли;
// Быстрое извлечение даты
РезультатДата = ДокументDOM.ВычислитьВыражениеXPath("/Данные/Шапка/Дата");
УзелДаты = РезультатДата.ПолучитьСледующий();
Если УзелДаты <> Неопределено Тогда
Объект.ДатаДокумента = XMLЗначение(Тип("Дата"), УзелДаты.ТекстовоеСодержимое);
КонецЕсли;
УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры
Использование XPath делает код чистым, лаконичным и позволяет мгновенно адаптироваться под изменения в структуре XML-файла: достаточно просто поправить путь в строке запроса.
Если структура вашего XML-файла строго регламентирована и известна заранее (например, у вас есть схема XSD), наиболее технологичным решением будет использование встроенного механизма 1С — XDTO (XML Data Transfer Objects). С его помощью можно преобразовать XML-файл в объектную модель 1С, где к тегам можно обращаться напрямую как к свойствам объекта через точку (например, ОбъектXDTO.Организация).
Посмотрим на пример загрузки через глобальную фабрику XDTO:
&НаСервере
Процедура ЗаполнитьЧерезXDTO(АдресХранилища)
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяВременногоФайла);
Попытка
// Читаем XML напрямую в объект XDTO
ДанныеФайлаXDTO = ФабрикаXDTO.ПрочитатьXML(Чтение);
// Теперь мы можем обращаться к свойствам объекта напрямую
Объект.СуммаОперации = ДанныеФайлаXDTO.Сумма;
Объект.Комментарий = ДанныеФайлаXDTO.Примечание;
// Поиск ссылочных типов
НайденныйСклад = Справочники.Склады.НайтиПоНаименованию(ДанныеФайлаXDTO.СкладНаименование);
Если Не НайденныйСклад.Пустая() Тогда
Объект.Склад = НайденныйСклад;
КонецЕсли;
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка разбора XDTO: " + ОписаниеОшибки());
КонецПопытки;
Чтение.Закрыть();
УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры
Если теги внутри вашего XML-файла названы точно так же, как и реквизиты вашей обработки на форме (например, тег <Организация> соответствует реквизиту Организация), вы можете существенно сократить написание рутинного кода. Соберите все прочитанные данные в обычную Структуру, где ключами будут имена тегов, а затем воспользуйтесь стандартной процедурой платформы:
// Создаем структуру, ключи которой совпадают с реквизитами обработки
СчитанныеДанные = Новый Структура;
СчитанныеДанные.Вставить("Организация", ВыгруженнаяОрганизация);
СчитанныеДанные.Вставить("Контрагент", ВыгруженныйКонтрагент);
СчитанныеДанные.Вставить("СуммаОперации", ВыгруженнаяСумма);
// Одной строкой заполняем все реквизиты обработки (ЭтотОбъект)
ЗаполнитьЗначенияСвойств(Объект, СчитанныеДанные);
Метод ЗаполнитьЗначенияСвойств автоматически сопоставит ключи структуры с реквизитами объекта 1С и присвоит им соответствующие значения, избавив вас от написания десятков строк однотипных присвоений.
В некоторых случаях разработчики тратят много времени на написание XML-импорта только ради того, чтобы пользователь не заполнял форму заново каждый раз при открытии. Если это ваш случай, знайте: в платформе 1С:Предприятие уже реализован мощный стандартный механизм автосохранения настроек, не требующий написания ни одной строки кода!
Выполним простую пошаговую настройку в Конфигураторе:
АвтоматическоеСохранениеДанныхФормыВНастройках в значение Использовать.После этого платформа 1С автоматически начнет запоминать последние введенные пользователем данные. При следующем открытии обработки все поля автоматически заполнятся значениями из предыдущей сессии работы. Более того, пользователь сможет самостоятельно сохранять различные шаблоны заполнения через стандартное меню формы Еще — Сохранить параметры / Восстановить параметры.