Сохранение реквизитов и табличных частей объектов в 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 не будет опубликован. Обязательные поля помечены *