Сохранение реквизитов и табличных частей объектов в XML

Пример того, как можно сохранять и загружать объекты 1С методами встроенных объектов ЗаписьXML и ЧтениеXML.

Представленные в публикации процедуры были рождены потребностью сохранять настройки внешних обработок с возможностью последующего их использования на других компьютерах и в других информационных базах. Работают они, однако, не только для обработок, но и для любых других объектов 1С, имеющих реквизиты и табличные части. Сам я пользуюсь ими достаточно часто, при решении самых различных задач, потому решил поделиться, глядишь кому-нибудь пригодится. Так же, думаю, этот материал может быть полезен начинающим программистам как пример рекурсивного чтения структуры XML-файла, к тому же процедуры малы, просты и легко «допиливаются» под специфические задачи.

Сохранение производится процедурой СохранитьРеквизитыИТабличныеЧасти. В качестве параметров она принимает сохраняемый объект и имя файла. Ссылочные реквизиты сохраняются в виде GUID.

Процедура СохранитьРеквизитыИТабличныеЧасти (Объект, ИмяФайлаXML=Неопределено) Экспорт
   
ФайлXML = Новый ЗаписьXML;
   
ФайлXML.ОткрытьФайл(ИмяФайлаXML);
   
ФайлXML.ЗаписатьОбъявлениеXML();
   
ФайлXML.ЗаписатьНачалоЭлемента(«Root»);
   
ФайлXML.ЗаписатьАтрибут(«Объект»,Объект.Метаданные().Имя);
//Сохраняем реквизиты
   
Для Каждого Реквизит Из Объект.Метаданные().Реквизиты Цикл
       
ФайлXML.ЗаписатьНачалоЭлемента(«Реквизит»);
       
ФайлXML.ЗаписатьАтрибут(«Имя», Реквизит.Имя);
       
ТипЗначения = ТипЗнч(Объект[Реквизит.Имя]);
        Если Не
ТипЗначения = Тип(«Неопределено») Тогда
           
ФайлXML.ЗаписатьАтрибут(«ИмяТипа», XMLТип(ТипЗначения).ИмяТипа);
           
ФайлXML.ЗаписатьАтрибут(«URI», XMLТип(ТипЗначения).URIПространстваИмен);
        КонецЕсли;
       
ФайлXML.ЗаписатьТекст(XMLСтрока(Объект[Реквизит.Имя]));
       
ФайлXML.ЗаписатьКонецЭлемента();
    КонецЦикла;
//Сохраняем табличные части
   
Для Каждого ТЧ из Объект.Метаданные().ТабличныеЧасти Цикл
       
ФайлXML.ЗаписатьНачалоЭлемента(«ТабличнаяЧасть»);
       
ФайлXML.ЗаписатьАтрибут(«Имя», ТЧ.Имя);
        Для Каждого
СтрокаТЧ из Объект[ТЧ.Имя] Цикл
           
ФайлXML.ЗаписатьНачалоЭлемента(«ЭлементКоллекции»);
            Для Каждого
РеквизитТЧ Из ТЧ.Реквизиты Цикл
               
ФайлXML.ЗаписатьНачалоЭлемента(«Реквизит»);
               
ФайлXML.ЗаписатьАтрибут(«Имя», РеквизитТЧ.Имя);
               
ТипЗначения = ТипЗнч(СтрокаТЧ[РеквизитТЧ.Имя]);
                Если Не
ТипЗначения = Тип(«Неопределено») Тогда
                   
ФайлXML.ЗаписатьАтрибут(«ИмяТипа», XMLТип(ТипЗначения).ИмяТипа);
                   
ФайлXML.ЗаписатьАтрибут(«URI», XMLТип(ТипЗначения).URIПространстваИмен);
                КонецЕсли;
               
ФайлXML.ЗаписатьТекст(XMLСтрока(СтрокаТЧ[РеквизитТЧ.Имя]));
               
ФайлXML.ЗаписатьКонецЭлемента();
            КонецЦикла;
           
ФайлXML.ЗаписатьКонецЭлемента();
        КонецЦикла;
       
ФайлXML.ЗаписатьКонецЭлемента();
    КонецЦикла;
    Если Не
ФайлXML=Null Тогда
       
ФайлXML.ЗаписатьКонецЭлемента();
       
ФайлXML.Закрыть();
    КонецЕсли;
КонецПроцедуры

За чтение объекта отвечают процедуры ЗагрузитьРеквизитыИТабличныеЧасти и ЗагрузитьОбъектРекурсивно. Чтобы прочитать объект вызывается первая, ей передаются объект, который необходимо заполнить, и имя файла. Вторая является вспомогательной.

Процедура ЗагрузитьРеквизитыИТабличныеЧасти(Объект, ИмяФайлаXML=Неопределено) Экспорт
    Если Не
ИмяФайлаXML = Неопределено Тогда
       
ФайлXML = Новый ЧтениеXML;
       
ФайлXML.ОткрытьФайл(ИмяФайлаXML);
        Пока
ФайлXML.Прочитать() Цикл
            Если
ФайлXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
               
ЗагрузитьОбъектРекурсивно(ФайлXML, Объект, ФайлXML.Имя);
            КонецЕсли
        КонецЦикла;
    КонецЕсли;
КонецПроцедуры

Процедура ЗагрузитьОбъектРекурсивно(ФайлXML, Объект, знач ИмяУзла)
   
ИмяТипа = «»;
   
ПространствоИмен = «»;
    Пока
ФайлXML.ПрочитатьАтрибут() Цикл
        Если
ФайлXML.Имя = «ИмяТипа» Тогда
           
ИмяТипа = ФайлXML.Значение;
        ИначеЕсли
ФайлXML.Имя = «URI» Тогда
           
ПространствоИмен = ФайлXML.Значение;
        КонецЕсли;
    КонецЦикла;
    Пока
ФайлXML.Прочитать() Цикл
        Если
ФайлXML.ТипУзла = ТипУзлаXML.КонецЭлемента И ФайлXML.Имя = ИмяУзла Тогда
            Возврат;
        ИначеЕсли
ФайлXML.ТипУзла = ТипУзлаXML.Текст Тогда
           
ТипОбъекта = ИзXMLТипа(ИмяТипа, ПространствоИмен);
            Если НЕ
ТипОбъекта = Неопределено тогда
               
Объект = XMLЗначение(ТипОбъекта, ФайлXML.Значение);
            КонецЕсли;
        ИначеЕсли
ФайлXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
           
ИмяТекУзла = ФайлXML.Имя;
            Если
ФайлXML.Имя = «ЭлементКоллекции» Тогда
               
ЗагрузитьОбъектРекурсивно(ФайлXML, Объект.Добавить(), ИмяТекУзла);
            Иначе
                Если
ФайлXML.ПрочитатьАтрибут() Тогда
                   
ЗагрузитьОбъектРекурсивно(ФайлXML, Объект[ФайлXML.Значение], ИмяТекУзла);
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

В приложенном файле демонстрационная обработка, позволяющая сохранить ссылочный объект БД в файл и заполнить объект из файла. Заполняемый объект должен быть того же типа, что и сохраненный, а также он должен быть предварительно создан, сохранен в ИБ и выбран в поле «Ссылка».

20 Comments

  1. Angeros

    Конечно + но все это есть в выгрузка загрузка хмл. стандартной обработке.

    Reply ↓
  2. saiten
    все это есть в выгрузка загрузка хмл. стандартной обработке

    Конечно, там это есть, и даже гораздо больше. Я же только лишь предложил минимальный механизм, который без лишних сложностей можно использовать в собственных разработках.

    Reply ↓
  3. Поручик

    Рекомендую как пример работы с XML

    Reply ↓
  4. Seregalink

    Спасибо, очень хорошо, как пример вполне устраивает!

    Reply ↓
  5. Gasdrubal

    Отличный универсальный механизм. А нельзя как — то также регистры выгружать или там планы счетов? Выложите может код?

    Reply ↓
  6. saiten

    (5) Как-то необходимости не возникало. С регистрами много вопросов: весь регистр выкидывать или по отбору? или по запросу? и как загружать: только дописывать или очищать имеющиеся записи? Если бы мне поставили такую задачу — я бы сделал обработку с табличной частью, колонки которой повторяют структуру данных регистра, запросом выбрал бы нужные записи в ТЧ, а ее уже потом приведенным кодом выкинул в XML. Хотя, можно и непосредственно регистр обрабатывать. Почему, собственно, нет? Будет время — посмотрю.

    Добавлено:

    А лучше использовать универсальную выгрузку/загрузку XML — это задача самое для нее.

    Reply ↓
  7. saiten

    (7) Пожалуйста. Рад, что кому-то пригодилось.

    Reply ↓
  8. artmicro

    Хм, Автор скорее всего не слышах про встроенный сериализатор, который позволяет сделать все тоже — только используя всего две строчки кода.

    Reply ↓
  9. zog
    artmicro пишет:

    Хм, Автор скорее всего не слышах про встроенный сериализатор, который позволяет сделать все тоже — только используя всего две строчки кода.

    поумничать решили?) автор все слышал, я уверен, для новичков (таких как я) пример в самый раз.

    Reply ↓
  10. saiten

    (9) Попробуйте сериализовать внешнюю обработку. Собственно, именно для сохранения реквизитов и ТЧ обработок этот код и писался. То, что можно выгружать и другие объекты БД — это побочный эффект.

    Кроме того, не всегда можно обойтись сериализацией. Например, если идёт обмен XML-файлами со сторонней системой. В этом случае надо работать методами объектов Чтение-/ЗаписьXML. Можно, в принципе, и через DOM, но, честно говоря, не знаю, насколько реализация этой технологии в 1С справляется с большими файлами.

    Reply ↓
  11. artmicro

    (10) Ну чего же поумничать. Просто в последнее время, на этом ресурсе все больше и больше тем которые описаны в типовых книгах 1С…

    Reply ↓
  12. artmicro

    (11) Согласен, внешнюю обработку сериализовать штатными средствами не получится. Но можно использовать механизм хранения настроек в БД. Но я, если честно, не могу себе представить задачу где обходимо было бы сериализовать внешнюю обработку %)

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

    Reply ↓
  13. saiten

    (13)

    Но я, если честно, не могу себе представить задачу где обходимо было бы сериализовать внешнюю обработку %)

    Сохранение настроек средствами платформы, к сожалению, не отличается надёжностью. Хранить же настройки в БД не всегда целесообразно.

    Данный механизм может использоваться

    1. для надёжного хранения настроек вне базы;

    2. если обработка содержит большое количество настроек, ну, например, загрузка данных сложной структуры из XLS-файла: сопоставили ячейки документа данным, обкатали на тестовой базе, сохранили настройки в XLS, перенесли в рабочую;

    3. если нескольким пользователям необходимо работать с общим набором настроек;

    4. транспорт данных в тех случаях, когда нецелесообразно заморачиваться с конвертацией;

    5. внешнее хранение небольших объемов данных.

    Reply ↓
  14. artmicro

    (14) Надежность хранение настроек в файлах, тоже довольно сомнительный момент. Но в целом Вы правы. Суть понятна. Спасибо.

    Reply ↓
  15. lsp71

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

    Reply ↓
  16. DrAku1a

    (14) А так сохранить настройки не проще?

    ЗначениеВФайл(<ИмяФайла>, ТабличнаяЧасть.Выгрузить())
    Reply ↓
  17. saiten

    (18)Ага, проще конечно. Но не всегда. Реквизиты так не выгрузить; если табличных частей больше одной — потребуется несколько файлов. В общем, каждой задаче — свой инструмент.

    Reply ↓
  18. v.a.ryag

    Плюсую за простоту и доступность представления информации. Взял себе код на заметку)

    Reply ↓
  19. serq82

    а как можно сделать с внешней обработкой, в ней реквизиты и табличные части выгружались???не сохраняя данные обработки, а также как документ.ве дь в обработке не получишь Объект.Метаданные()

    Reply ↓
  20. saiten

    (21)Ну почему же? У внешних обработок есть метаданные. Собственно, для обработок этот код и писался. Все работает.

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *