Каталог решений - Новичок новичку: как выгрузить/загрузить XML данные

Новичок новичку: как выгрузить/загрузить XML данные

Новичок новичку: как выгрузить/загрузить XML данные

В наличии

Рассказываю на примере, как с помощью xml файлов выгружать/загружать метаданные (документы, справочники, регистры). Рассматривается клиент-серверный вариант. Вариант передачи файла на сервер. Вариант загрузки файла без создания временных файлов.

Категория:

Описание

Здравствуйте.

Перед Вами возникла задача загрузки, выгрузки метаданных (документы, справочники, регистры…).

Возникнуть такая задача может по разным причинам. Как правило — это бэкап и перенос из базы в базу. Сейчас нас не интересует причина, просто примем как данность: потребовалась выгрузка, а затем последующая загрузка. Ее мы и будем сегодня выполнять.

Прежде всего, сразу хочу Вас остановить бежать открывать конфигуратор. Есть прекрасный инструмент, созданный самой фирмой 1С, который называется ВыгрузкаЗагрузкаДанныхXML83.epf (есть и ВыгрузкаЗагрузкаДанныхXML82.epf). Этим инструментом можно воспользоваться, если требуется перенести вообще все записи в метаданных, например, все документы товаров и услуг или все строки в справочнике номенклатура. Однако тонкой настройки выгрузки и загрузки Вы не при этом не получите. Выгрузятся все документы, а не конкретный или за период, а при загрузке, если документ уже такой есть, вы можете получить или задвоение, или сообщение об ошибке (которую, впрочем можно игнорировать). В принципе, все решаемо, но потребует более осмысленных действий, чем просто нажатие на кнопку.

Для более тонкой настройки выгрузки/загрузки можно использовать конфигурацию «Конвертация». Выгружать /загружать тут можно что угодно и как угодно, но изучение данного продукта потребует не одной недели практики.

А у Вас ситуация: нужно срочно что-то выгрузить / загрузить и некогда разбираться c существующими инструментами, а конфигуратор открывать Вы умеете, или нужно доработать уже существующий механизм, или разработать свой для пользователя, который не хочет разбираться в Конвертации, а хочет иметь «волшебную» кнопку, которая сделает все за него.

Для начала выполнения кода по загрузке или выгрузке нам нужно 2 кнопки: по нажатии на одну будет происходить выгрузка, а по нажатии на другую будет происходить загрузка. Где они будут расположены не важно, для примера, создадим обработку, в которую добавим форму, а на форму закинем эти 2 кнопки.

Формат выгрузки выберем xml, это стандарт и будем ему следовать, да и обрабатывать такой файл легче, чем, допустим, текстовой.

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

В результате у Вас должна получиться форма похожая на:

 

 

Переходим в модуль формы и вставляем следующий текст:

  
  &НаКлиенте
  Процедура ПриОткрытии(Отказ)
  	НачалоПериода = НачалоГода(ТекущаяДата());
  	КонецПериода = КонецГода(ТекущаяДата());
  КонецПроцедуры
  
  &НаКлиенте
  Процедура Выгрузить(Команда)
  	АдресФайлаВоВременномХранилище = "";
  	ВыгрузитьНаСервере(АдресФайлаВоВременномХранилище);
  	ПолучитьФайл(АдресФайлаВоВременномХранилище, "Выгрузка.xml");
  КонецПроцедуры
  
  &НаСервере
  Функция ВыгрузитьНаСервере(АдресФайлаВоВременномХранилище)
  	Расширение = ".xml";
  	ИмяВременногоФайла = ПолучитьИмяВременногоФайла(Расширение);
  	
  	Запрос = Новый Запрос;
  	Запрос.Текст = 
  	"ВЫБРАТЬ
  	|	ФП_Месячный.Номер КАК Номер,
  	|	ФП_Месячный.Дата КАК Дата,
  	|	ФП_Месячный.Период КАК Период,
  	|	ФП_Месячный.Статьи.(
  	|		НомерСтроки КАК НомерСтроки,
  	|		Вид.Код КАК ВидКод,
  	|		ПланГодовой КАК ПланГодовой,
  	|		ПланМесячный КАК ПланМесячный
  	|	) КАК Статьи
  	|ИЗ
  	|	Документ.ФП_Месячный КАК ФП_Месячный
  	|ГДЕ
  	|	ФП_Месячный.ПометкаУдаления = ЛОЖЬ
  	|	И ФП_Месячный.Дата >= &Дата1
  	|	И ФП_Месячный.Дата <= &Дата2";
  	
  	Запрос.УстановитьПараметр("Дата1", НачалоПериода);
  	Запрос.УстановитьПараметр("Дата2", КонецПериода);	
  	Выборка = Запрос.Выполнить().Выгрузить();
  	
  	//заполняем файло	
  	ЗаписьXML = Новый ЗаписьXML;
  	ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла,"UTF-8");
  	ЗаписьXML.ЗаписатьОбъявлениеXML();
  	ЗаписьXML.ЗаписатьНачалоЭлемента("Выгрузка");
  	Для Каждого СТР Из Выборка Цикл
  		ЗаписьXML.ЗаписатьНачалоЭлемента("НовыйДокумент");
  		
  		ЗаписьXML.ЗаписатьНачалоЭлемента("Номер");
  		ЗаписьXML.ЗаписатьТекст(XMLСтрока(СТР.Номер));
  		ЗаписьXML.ЗаписатьКонецЭлемента();
  		
  		ЗаписьXML.ЗаписатьНачалоЭлемента("Дата");
  		ЗаписьXML.ЗаписатьТекст(XMLСтрока(СТР.Дата));
  		ЗаписьXML.ЗаписатьКонецЭлемента();
  		
  		ЗаписьXML.ЗаписатьНачалоЭлемента("Период");
  		ЗаписьXML.ЗаписатьТекст(XMLСтрока(СТР.Период));
  		ЗаписьXML.ЗаписатьКонецЭлемента();
  		
  		Для Каждого ТЧ Из СТР.Статьи Цикл
  			ЗаписьXML.ЗаписатьНачалоЭлемента("НоваяСтатья");
  			
  			ЗаписьXML.ЗаписатьНачалоЭлемента("ВидКод");
  			ЗаписьXML.ЗаписатьТекст(XMLСтрока(ТЧ.ВидКод));
  			ЗаписьXML.ЗаписатьКонецЭлемента();
  			
  			ЗаписьXML.ЗаписатьНачалоЭлемента("ПланГодовой");
  			ЗаписьXML.ЗаписатьТекст(XMLСтрока(ТЧ.ПланГодовой));
  			ЗаписьXML.ЗаписатьКонецЭлемента();
  			
  			ЗаписьXML.ЗаписатьНачалоЭлемента("ПланМесячный");
  			ЗаписьXML.ЗаписатьТекст(XMLСтрока(ТЧ.ПланМесячный));
  			ЗаписьXML.ЗаписатьКонецЭлемента();
  			
  			ЗаписьXML.ЗаписатьКонецЭлемента();
  		КонецЦикла;
  		ЗаписьXML.ЗаписатьКонецЭлемента();
  	КонецЦикла;
  	ЗаписьXML.ЗаписатьКонецЭлемента();
  	ЗаписьXML.Закрыть();
  	//заполняем файло	
  	
  	Файл = Новый Файл(ИмяВременногоФайла);	
  	Если Файл.Существует() Тогда		
  		ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла);
  		АдресФайлаВоВременномХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанные, УникальныйИдентификатор);
  		УдалитьФайлы(ИмяВременногоФайла);		
  	КонецЕсли;	
  
  КонецФункции
  
  
  &НаКлиенте
  Процедура Загрузить(Команда)
  	ПриЗавершении = Новый ОписаниеОповещения("ПоместитьФайлКомандаЗавершение", ЭтотОбъект);
  	ПередПомещением = Новый ОписаниеОповещения("ПоместитьФайлКомандаПередПомещением", ЭтотОбъект);
  	ДиалогВыбораФайлов = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
  	ДиалогВыбораФайлов.Заголовок = "Выбор файла для помещения";
  	ДиалогВыбораФайлов.МножественныйВыбор = Ложь;
  	ДиалогВыбораФайлов.Фильтр = "Файлы выгрузки|*.xml";
  	НачатьПомещениеФайла(ПриЗавершении, , ДиалогВыбораФайлов, Истина, , ПередПомещением);
  КонецПроцедуры
  
  &НаКлиенте
  Процедура ПоместитьФайлКомандаЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
  	Если Результат Тогда
  		ОбработатьНаСервере(Адрес);
  		ОповеститьОбИзменении(Тип("ДокументСсылка.ФП_Месячный"));
  		Сообщить("Загрузка завершена");
  	Иначе
  		Сообщить("Всетаки нужно было что-то выбрать");
  	КонецЕсли;
  КонецПроцедуры
  
  &НаКлиенте
  Процедура ПоместитьФайлКомандаПередПомещением(ПомещаемыйФайл, ОтказОтПомещенияФайла, ДополнительныеПараметры) Экспорт
  	Если ПомещаемыйФайл.Размер() > 4*1024*1024 Тогда
  		ПоказатьПредупреждение(, "Превышен максимальный размер файла");
  		ОтказОтПомещенияФайла = Истина;
  	КонецЕсли;
  	Файл = ПомещаемыйФайл.Файл.ПолноеИмя;
  КонецПроцедуры
  
  &НаСервереБезКонтекста
  Процедура ОбработатьНаСервере(Адрес)
  	Данные = ПолучитьИзВременногоХранилища(Адрес);
  	Парсер = Новый ЧтениеXML;
  	Парсер.УстановитьСтроку(ПолучитьСтрокуИзДвоичныхДанных(Данные,КодировкаТекста.UTF8));
  	Пока Парсер.Прочитать() Цикл
  		
  		Если (Парсер.Имя = "НовыйДокумент") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			НовыйДокумент = Документы.ФП_Месячный.СоздатьДокумент();
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "НовыйДокумент") И (Парсер.ТипУзла = ТипУзлаXML.КонецЭлемента) Тогда
  			Попытка
  				НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
  			Исключение
  			КонецПопытки;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "Номер") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НовыйДокумент.Номер = Парсер.Значение;
  			КонецЕсли;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "Дата") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НовыйДокумент.Дата = XmlЗначение(Тип("Дата"), Парсер.Значение);
  			КонецЕсли;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "Период") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НовыйДокумент.Период = ПредопределенноеЗначение("Перечисление.ФП_Месяцы." + Парсер.Значение);
  			КонецЕсли;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "НоваяСтатья") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			НоваяСтрокаДокумента = НовыйДокумент.Статьи.Добавить();
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "ВидКод") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НоваяСтрокаДокумента.Вид = Справочники.ФП_СтатьиДвижения.НайтиПоКоду(Парсер.Значение);
  			КонецЕсли;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "ПланГодовой") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НоваяСтрокаДокумента.ПланГодовой = Парсер.Значение;
  			КонецЕсли;
  		КонецЕсли;
  		
  		Если (Парсер.Имя = "ПланМесячный") И (Парсер.ТипУзла = ТипУзлаXML.НачалоЭлемента) Тогда
  			Если Парсер.Прочитать() Тогда
  				НоваяСтрокаДокумента.ПланМесячный = Парсер.Значение;
  			КонецЕсли;
  		КонецЕсли;
  		
  	КонецЦикла; 
  	Парсер.Закрыть();
  	
  КонецПроцедуры
  

Возвращаемся в форму. Переходим во вкладку команд формы. И указываем для команды выгрузить действие выгрузить, для команды загрузить действие загрузить:

 

 

В принципе, для моей конфигурации обработка уже рабочая. Но под Ваши задачи ее нужно переделать. А для этого разберем код.

Процедура ПриОткрытии устанавливает по умолчанию даты периода. Чтобы процедура срабатывала при открытии формы, ее следует указать в соответствующем событии формы.

 

 

Делать это не обязательно. Просто для удобства пользования.

Небольшое отступление.

Прежде всего, нам нужно понять, что наш персональный компьютер и сервер в большинстве случаев — это разные компьютеры. По крайней мере так рекомендовано. 1С и я вместе с ними будем исходить из того, что вы работаете за клиентским ПК, а исполняемый код и база данных находятся на другом — сервере.

Продолжаем исходить из того, что сервер — это отдельный компьютер (даже если это не так, но для универсальности кода придется так считать — Ваш ПК будет сервером для самого себя).

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

Соответственно, код по добавлению, изменению, чтению, будет выполняться на ДРУГОМ ПК. Не на Вашем!

А так как файлы сервера — это не Ваши файлы, а Ваши файлы — это не файлы сервера. То нам нужно какое-то общее и для Вас и для сервера хранилище файлов. Вот в нем и будут происходить все операции. Такое хранилище называется временным.

Разбираем код выгрузки.

Команду выгрузки я подглядел в обработке ВыгрузкаЗагрузкаДанныхXML83.epf, о которой я говорил выше. Так что выгрузка у нас будет вполне себе типовая (правда я выкинул кучу строк ненужного кода, чем облегчил Вам труд)

Первым делом на клиенте создаем общее хранилище:

АдресФайлаВоВременномХранилище, так как мы не знаем ничего про него, то присваиваем ему пустой адрес.

Затем строчкой ВыгрузитьНаСервере(АдресФайлаВоВременномХранилище); мы заполняем наше временное хранилище данными которые хотим сохранить в виде файла.

Строкой ПолучитьФайл(АдресФайлаВоВременномХранилище, "Выгрузка.xml"); мы забираем из временного хранилища на свой компьютер данные, которые можно или посмотреть или сохранить в виде файла с названием Выгрузка.xml. Это стандартная процедура. Работает везде одинаково и разбору не подлежит.

А вот процедуру ВыгрузитьНаСервере(АдресФайлаВоВременномХранилище) разберем подробнее. Именно в ней происходит создание и заполнение временного файла обмена.

На сервере еще нет нужного нам файла, а потому первыми двумя строками мы определяем временный файл на сервере. Код выполняется на сервере, а потому временный файл тоже будет на сервере. Его мы заполним и поместим во временное хранилище из которого уже заберем себе на клиента.

Далее запросом получаем данные, которые необходимо выгружать в файл. Для примера, взят абстрактый документ, который называется ФП_Месячный, у него есть реквизиты: Дата, Номер, Период и табличная часть: Статьи.

Далее идет блок заполнения временного файла данными из запроса.

Первым делом мы объявляем новую ЗаписьXML. С помощью нее будет производиться запись во временный файл: ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла,"UTF-8");

UTF-8 — это кодировка текста. Ее можно принять как стандарт для большинства случаев.

ЗаписьXML.ЗаписатьОбъявлениеXML() — записываем во временный файл служебную информацию, например, информацию о кодировке. Тут думать не надо. Метод стандартный, все что нужно сделает сам.

Далее запись информации идет по принципу скобок в тексте. Есть открывающая, есть закрывающая и что-то в середине. Можно вкладывать одни элементы в другие:

Открываем элемент: ЗаписьXML.ЗаписатьНачалоЭлемента("НазваниеЭлемента");

//что-то делаем, конкретно в нашем случае, мы делаем запись о реквизите документа либо о самом документе

Закрываем элемент: ЗаписьXML.ЗаписатьКонецЭлемента()

Во время записи активно пользуемся функцией XMLСтрока(), которая преобразует метаданные в строковой тип, понятный для записи/чтения XML.

Так как сохранять данные мы можем только простые (число, дата, строка, булево…). То ссылочный тип данных Вид (это справочник с видами статей) мы сохранить не можем. Но мы можем сохранить Код Вида из справочника видов — это строка. Когда мы будем наоборот загружать данные из ХML, то мы будем получать Код. По коду искать ссылку в справочнике. И уже эту ссылку записывать в документ.

После того как документ xml сформирован (а именно это мы и делали). Мы ЗаписьXML помещаем в файл, хотя по сути, он и так временный файл, проверяем всели у нас получилось, и если да, то помещаем данные во временное хранилище. Почему это так сделано, я не знаю, механизм типовой.

Ну и последним делом, мы удаляем на сервере временный файл. Если это не сделать, то сервер быстро переполниться временными файлами.

Вообще ситуация с временными файлами мне не нравится. Если загрузка/выгрузка будет идти постоянно, то накопитель сервера быстро выйдет из строя. Но так сделано разработчиками 1С. Этот код я подсмотрел у них в стандартной обработке.

Если можете подсказать как сделать все в оперативной памяти буду благодарен.

Разбираем код загрузки.

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

Вообще варианта 2:

1. Простой. Считать xml файл в переменную (массив) на клиенте. А после передав эту переменную на сервер, читая данные из массива записывать новые документы.

2. Интересный. Передать xml файл на сервер и читать строки из него в оперативной памяти, не используя файлы вообще.

Оба этих метода требуют значительного количества памяти на сервере, а потому рекомендуется делать проверку на количество передаваемой информации. И если ее много, то всеже использовать файловый вариант и читать из файла блоками.

Рассмотрим вариант 2.

Помещать файл выгрузки на сервер во временное хранилище мы будем стандартной процедурой НачатьПомещениеФайла(). Так как механизм типовой, я просто скопировал код из примера 1С.

Для всех он будет одинаков. Единственно что я поменял, это фильтр (сделал xml). Стоит еще обратить внимание на размер. В моем случае он равен 4Гб. В Вашем, можете сделать меньше (больше нельзя).

Процедуру ПоместитьФайлКомандаЗавершение() переписал полностью. В ней я смотрю был ли выбран файл. Если файл был выбран, если он удовлетворяет размеру и расширению, тогда будет происходить чтение из данных указного файла в процедуре ОбработатьНаСервере(Адрес), где Адрес — это адрес временного хранилища. Ну а после того, как мы добавим данные, следует попросить систему перечитать базу, так как данные изменились. Это мы делаем процедурой ОповеститьОбИзменении(Тип("ДокументСсылка.ФП_Месячный")). Делать это не обязательно, но тогда чтобы увидеть результат нам придется закрыть и снова открыть список документов ФП_Месячный.

Рассмотрим процедуру ОбработатьНаСервере(Адрес)

Первым делом на сервере из временного хранилища нужно получить то, что мы помещали на клиенте: Данные = ПолучитьИзВременногоХранилища(Адрес);

Объявляем новую переменную с типом ЧтениеXML. В ней будет храниться весь документ XML.

Хитрая строка Парсер.УстановитьСтроку(ПолучитьСтрокуИзДвоичныхДанных(Данные,КодировкаТекста.UTF8)) преобразует двоичные данные в строку xml, которую затем записывает в объявленную ранее переменную с типом ЧтениеXML.

А далее читаем каждую строчку из XML и, в зависимости от данных, заполняем документ.

Ссылочные данные документа заполняются через их поиск по коду из xml.

Отступление.

Да возможность передачи ссылки имеется. Но я не рекомендую ее использовать, дабы данные не задвоились. То есть Документы с одинаковыми Кодами по сути будут разные так как у них разные ссылки (GUID). А если вести запись по коду документа, то документов с одинаковыми кодами Вы не получите. Будет сформировано исключительное событие и перенос данных в базу будет приостановлен. Поэтому я использую оператор попытка при записи документа. Если документ с таким кодом и датой уже есть, то запись произведена не будет, а будет переход к следующей записи.

PS. Благодаря подсказке Сергея, код преобразования данных в XML можно серьезно сократить. Просто замените функцию и процедуру. Будет менее понятно, что такое XML, как он должен заполняться и читаться, но ведь и не всем это надо:

  &НаСервере
  Функция ВыгрузитьНаСервере(АдресФайлаВоВременномХранилище)
  	
  	Запрос = Новый Запрос;
  	Запрос.Текст = 
  	"ВЫБРАТЬ
  	|	ФП_Месячный.Номер КАК Номер,
  	|	ФП_Месячный.Дата КАК Дата,
  	|	ФП_Месячный.Период КАК Период,
  	|	ФП_Месячный.Статьи.(
  	|		НомерСтроки КАК НомерСтроки,
  	|		Вид.Код КАК ВидКод,
  	|		ПланГодовой КАК ПланГодовой,
  	|		ПланМесячный КАК ПланМесячный
  	|	) КАК Статьи
  	|ИЗ
  	|	Документ.ФП_Месячный КАК ФП_Месячный
  	|ГДЕ
  	|	ФП_Месячный.ПометкаУдаления = ЛОЖЬ
  	|	И ФП_Месячный.Дата >= &Дата1
  	|	И ФП_Месячный.Дата <= &Дата2";
  	
  	Запрос.УстановитьПараметр("Дата1", НачалоПериода);
  	Запрос.УстановитьПараметр("Дата2", КонецПериода);	
  	Выборка = Запрос.Выполнить().Выгрузить();
  
  	//заполняем файло
  	ЗаписьXML = Новый ЗаписьXML;
  	ЗаписьXML.УстановитьСтроку();
  	СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Выборка);
  	//заполняем файло	
  	АдресФайлаВоВременномХранилище = ПоместитьВоВременноеХранилище(ЗаписьXML.Закрыть(), УникальныйИдентификатор);
  	
  КонецФункции
  

 

  &НаСервереБезКонтекста
  Процедура ОбработатьНаСервере(Адрес)
  	Данные = ПолучитьИзВременногоХранилища(Адрес);
  	ЧтениеXML = Новый ЧтениеXML;
  	ЧтениеXML.УстановитьСтроку(ПолучитьСтрокуИзДвоичныхДанных(Данные));
  	Объекты = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
  	Для Каждого Документ Из Объекты Цикл
  		Попытка
  			НовыйДокумент = Документы.ФП_Месячный.СоздатьДокумент();
  			НовыйДокумент.Номер = Документ.Номер;
  			НовыйДокумент.Дата = Документ.Дата;
  			НовыйДокумент.Период = Документ.Период;
  			Для Каждого Статья Из Документ.Статьи Цикл
  				НоваяСтрокаДокумента = НовыйДокумент.Статьи.Добавить();
  				НоваяСтрокаДокумента.Вид = Справочники.ФП_СтатьиДвижения.НайтиПоКоду(Статья.ВидКод);
  				НоваяСтрокаДокумента.ПланГодовой = Статья.ПланГодовой;
  				НоваяСтрокаДокумента.ПланМесячный = Статья.ПланМесячный;
  			КонецЦикла;
  			НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
  		Исключение
  		КонецПопытки;
  	КонецЦикла;
  КонецПроцедуры
  

 

has been added to your cart:
Оформление заказа