Поскольку рассматриваемый нами механизм выгрузки относится к разряду универсальных, подразумевается, что обмен данными производится между конфигурациями, имеющими разную структуру.
Рассмотрим простой пример обмена данными с различной структурой. Например, в базе узла-получателя в иерархическом справочнике Номенклатура присутствует дополнительный реквизит по сравнению с базой узла-отправителя, и наоборот.
В этом случае запись всех «общих» для обоих узлов реквизитов справочника должна производиться в ручном режиме. Порядок следования реквизитов справочника в конфигурации может не совпадать с порядком их записи в XML, но последующее чтение реквизитов из XML должно производиться в том же порядке, в каком они были записаны. Все остальные данные с идентичной структурой сериализуются в/из XML стандартным образом.
Для этого дополним процедуру ЗаписатьСообщениеСИзменениями() в модуле плана обмена (см. листинг 3.59) следующим образом (листинг 3.65).
Листинг 3.65. Процедура «ЗаписатьСообщениеСИзменениями»
Процедура ЗаписатьСообщениеСИзменениями() Экспорт
…
// Получить выборку измененных данных.
// *** Механизм регистрации изменений.
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(ЗаписьСообщения.Получатель, ЗаписьСообщения.НомерСообщения);
Пока ВыборкаИзменений.Следующий() Цикл
Данные = ВыборкаИзменений.Получить();
// Проверить, нужен ли перенос данных. Если нет, то записать удаление этих данных.
Если Не ОбменСУдаленнымиСкладами.НуженПереносДанных(ЗаписьСообщения.Получатель, Данные) Тогда
ОбменСУдаленнымиСкладами.УдалениеДанных(Данные);
КонецЕсли;
// Записать данные в сообщение *** XML-сериализация.
ОбменСУдаленнымиСкладами.ЗаписатьДанные(ЗаписьXML, Данные);
КонецЦикла;
…
КонецПроцедуры
Добавленный фрагмент выделен жирным шрифтом. В этом фрагменте данные, полученные в цикле перебора записей регистрации изменений, сериализуются в открытый XML-файл с помощью процедуры ЗаписатьДанные(), расположенной в общем модуле ОбменСУдаленнымиСкладами. В параметре Данные в процедуру передается полученный объект обмена (листинг 3.66).
Листинг 3.66. Процедура «ЗаписатьДанные»
Процедура ЗаписатьДанные(ЗаписьXML, Данные) Экспорт
Удаление = ?(ТипЗнч(Данные) = Тип("УдалениеОбъекта"), Истина, Ложь);
// Получить объект описания метаданного, соответствующий данным.
ОбъектМетаданных = ?(Удаление, Данные.Ссылка.Метаданные(), Данные.Метаданные());
Если Не Удаление И ОбъектМетаданных = Метаданные.Справочники.Номенклатура Тогда
// Записать элемент справочника вручную.
ЗаписатьXMLНоменклатура(ЗаписьXML, Данные);
Иначе
// Записать данные с помощью стандартного метода.
ЗаписатьXML(ЗаписьXML, Данные);
КонецЕсли
КонецПроцедуры
В процедуре производится проверка: если записывается не удаление данных и записываемый объект является элементом справочника Номенклатура, то вызывается процедура ЗаписатьXMLНоменклатура(), расположенная в общем модуле ОбменСУдаленнымиСкладами, для записи элемента справочника вручную (листинг 3.67). В противном случае запись производится с помощью стандартного метода ЗаписатьXML().
Листинг 3.67. Процедура «ЗаписатьXMLНоменклатура»
Процедура ЗаписатьXMLНоменклатура(ЗаписьXML, Товар )Экспорт
// Записать начало элемента XML.
ЗаписьXML.ЗаписатьНачалоЭлемента("CatalogObject.Номенклатура.Вручную");
// Ссылка
ЗаписатьXML(ЗаписьXML, Товар.Ссылка, "Ref", НазначениеТипаXML.Явное);
// ЭтоГруппа
ЗаписатьXML(ЗаписьXML, Товар.ЭтоГруппа, "IsFolder", НазначениеТипаXML.Явное);
// Родитель
ЗаписатьXML(ЗаписьXML, Товар.Родитель, "Parent", НазначениеТипаXML.Явное);
// Наименование
ЗаписатьXML(ЗаписьXML, Товар.Наименование, "Description", НазначениеТипаXML.Явное);
// Код
ЗаписатьXML(ЗаписьXML, Товар.Код, "Code", НазначениеТипаXML.Явное);
// Записать реквизиты, выгружаемые только для элемента справочника.
Если Не Товар.ЭтоГруппа Тогда
// ЗакупочнаяЦена
ЗаписатьXML(ЗаписьXML, Товар.ЗакупочнаяЦена, "ЗакупочнаяЦена", НазначениеТипаXML.Явное);
// ФайлКартинки
ЗаписатьXML(ЗаписьXML, Товар.ФайлКартинки, "ФайлКартинки", НазначениеТипаXML.Явное);
// Картинка
ЗаписатьXML(ЗаписьXML, Товар.Картинка, "Картинка", НазначениеТипаXML.Явное);
КонецЕсли;
// Записать конец элемента.
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецПроцедуры
В процедуре ЗаписатьXMLНоменклатура() производится «ручное» формирование элемента CatalogObject.Номенклатура.Вручную.
Сначала в открытый XML-файл мы записываем начало элемента с заголовком, указывающим на ручное формирование элемента. Затем при помощи метода ЗаписатьXML() записываем значения «общих» реквизитов, которые хотим передать с помощью обмена. Третьим параметром передаем строковое имя записываемого реквизита и в качестве четвертого параметра указываем значение перечисления НазначениеТипаXML, определяющее необходимость явного указания типа данных XML. После этого мы завершаем запись элемента и записываем в XML-файл конец элемента.
В процедуру ПрочитатьСообщенияСИзменениями() в модуле плана обмена (см. листинг 3.61) внесем следующие изменения (листинг 3.68).
Листинг 3.68. Процедура «ПрочитатьСообщениеСИзменениями»
Процедура ПрочитатьСообщениеСИзменениями() Экспорт
…
// Прочитать данные из сообщения в цикле. *** XML-сериализация.
Пока ОбменСУдаленнымиСкладами.ВозможностьЧтенияДанных(ЧтениеXML) Цикл
// Прочитать очередное значение.
Данные = ОбменСУдаленнымиСкладами.ПрочитатьДанные(ЧтениеXML);
// Записать полученные данные.
Данные.ОбменДанными.Отправитель = ЧтениеСообщения.Отправитель;
Данные.ОбменДанными.Загрузка = Истина;
Данные.Записать();
КонецЦикла;
…
КонецПроцедуры
Добавленный фрагмент выделен жирным шрифтом. В этом фрагменте в цикле мы выполняем чтение данных, содержащихся в сообщении обмена. При этом для каждого элемента в функции ВозможностьЧтенияДанных(), расположенной в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.69), проверяется возможность чтения данных. Если имя типа – CatalogObject.Товары.Вручную, возвращается значение Истина. В противном случае вызывается стандартный метод ВозможностьЧтенияXML(), с помощью которого из объекта ЧтениеXML получается очередной тип данных XML и определяется, имеется ли соответствующий тип «1С:Предприятия».
Листинг 3.69. Функция «ВозможностьЧтенияДанных»
Функция ВозможностьЧтенияДанных(ЧтениеXML)
// Получить тип данных XML, который может быть считан в данный момент.
ТипXML = ПолучитьXMLТип(ЧтениеXML);
Если ТипXML = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
Если ТипXML.ИмяТипа = "CatalogObject.Номенклатура.Вручную" И ТипXML.URIПространстваИмен = "" Тогда
Возврат Истина;
КонецЕсли;
Возврат ВозможностьЧтенияXML(ЧтениеXML);
КонецФункции
В цикле чтения данных в процедуре ПрочитатьСообщениеСИзменениями() вызывается функция ПрочитатьДанные(), расположенная в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.70), в которой производится чтение данных из элемента. При этом если читается элемент CatalogObject.Номенклатура.Вручную, то вызывается функция ПрочитатьXMLНоменклатура(), расположенная в общем модуле ОбменСУдаленнымиСкладами (см. листинг 3.71) для «ручного» чтения данных, в противном случае чтение осуществляется с помощью стандартного метода ПрочитатьXML.
Листинг 3.70. Функция «ПрочитатьДанные»
Функция ПрочитатьДанные(ЧтениеXML)
ТипXML = ПолучитьXMLТип(ЧтениеXML);
Если ТипXML = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Если ТипXML.ИмяТипа = "CatalogObject.Номенклатура.Вручную" И ТипXML.URIПространстваИмен = "" Тогда
// Прочитать значение справочника Номенклатура.
Возврат ПрочитатьXMLНоменклатура(ЧтениеXML);
КонецЕсли;
// Прочитать значение из объекта ЧтениеXML стандартным образом.
Возврат ПрочитатьXML(ЧтениеXML);
КонецФункции
Листинг 3.71. Функция «Прочитать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Номенклатура(), см. листинг 3.67.
В завершение проверяем: если считана вся информация о номенклатуре (достигнут конец элемента), то двигаемся на следующий элемент данных в сообщении обмена.
В результате функция возвращает данные о прочитанном элементе номенклатуры в процедуру ПрочитатьСообщениеСИзменениями(), в переменную Данные (см. листинг 3.68).
В случае стандартного чтения данных методом ПрочитатьXML переменная Данные будет содержать любой другой объект «1С:Предприятия», полученный из сообщения обмена.