К статье
Практический материал

Разработка и Конфигурация · 11 мин

Дерево значений в 1С: способы сохранения и восстановления

Как сохранить дерево значений в 1С: сериализация в XML и JSON, хранилище значения, файл обмена, передача между клиентом и сервером. Примеры кода и типичные ошибки.

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

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

Ниже разобраны основные способы: через объект ЗначениеВСтрокуВнутр, сериализацию в XML и JSON, хранилище значения в реквизите объекта, сохранение в файл на диск и передачу между клиентом и сервером в управляемых формах.

Почему дерево значений нельзя сохранить напрямую

Дерево значений (ДеревоЗначений) — несериализуемый сам по себе объект встроенного языка. Если попытаться присвоить его реквизиту справочника или документа, система выдаст ошибку несоответствия типов: в базе данных могут храниться только примитивные типы, ссылки и специальные контейнеры вроде ХранилищеЗначения.

Вторая особенность — дерево привязано к сеансу. Даже если объект объявлен в модуле, после перезапуска сеанса или переоткрытия формы его содержимое обнуляется. Поэтому любое «сохранение» дерева значений — это фактически преобразование его в строку, файл или двоичные данные с последующим восстановлением.

Третий нюанс касается управляемых форм: дерево, объявленное как локальная переменная модуля формы, не переживает серверные вызовы. Чтобы оно жило между вызовами, его нужно оформить как реквизит формы — тогда платформа сама заботится о передаче данных между клиентом и сервером в пределах сеанса формы.

Сохранение через ЗначениеВСтрокуВнутр

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

// Сохранение

СтрокаДерева = ЗначениеВСтрокуВнутр(ДеревоНоменклатуры);

// Восстановление

ДеревоНоменклатуры = ЗначениеИзСтрокиВнутр(СтрокаДерева);

Способ удобен для служебного хранения: результат можно положить в строковый реквизит неограниченной длины, в константу или в параметр сеанса через хранилище настроек. Недостаток — формат закрытый: прочитать такую строку может только сама платформа 1С, и совместимость между сильно различающимися версиями платформы не гарантируется.

Важное ограничение: если в дереве есть колонки типа ХранилищеЗначения или ссылочные значения, они сериализуются, но при восстановлении в другой информационной базе ссылки станут «битыми» — уникальные идентификаторы не совпадут. Для межбазового обмена этот способ не подходит.

Хранение в базе через ХранилищеЗначения

Когда дерево нужно привязать к конкретному объекту конфигурации — например, сохранить иерархию настроек для элемента справочника, — стандартное решение: добавить объекту реквизит типа ХранилищеЗначения и помещать в него дерево через конструктор Новый ХранилищеЗначения().

// Запись

ОбъектНастроек = Справочники.НастройкиПользователя.СоздатьЭлемент();

ОбъектНастроек.ДанныеДерева = Новый ХранилищеЗначения(ДеревоНоменклатуры, Новый СжатиеДанных(9));

ОбъектНастроек.Записать();

// Чтение

Дерево = ОбъектНастроек.ДанныеДерева.Получить();

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

Порядок сохранения дерева в базу
  1. 1
    Добавьте объекту конфигурации реквизит типа ХранилищеЗначения
  2. 2
    Поместите дерево: Новый ХранилищеЗначения(Дерево)
  3. 3
    Вызовите Записать() у объекта
  4. 4
    При чтении вызовите Реквизит.Получить() и проверьте тип результата
  5. 5
    Проверьте восстановленное дерево: количество строк и наличие колонок

Типичная ошибка — забыть вызвать Получить() и работать с самим хранилищем как с деревом. Признак проблемы: попытка обратиться к Строки у объекта хранилища вызывает ошибку «Поле объекта не обнаружено».

Выгрузка дерева в XML и JSON

Для обмена с внешними системами дерево сериализуют в открытые форматы. Встроенный СериализаторXDTO умеет записывать дерево значений в XML штатно:

ЗаписьXML = Новый ЗаписьXML;

ЗаписьXML.УстановитьСтроку();

СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ДеревоНоменклатуры);

СтрокаXML = ЗаписьXML.Закрыть();

Обратное чтение выполняется через СериализаторXDTO.ПрочитатьXML(). Способ подходит для передачи между базами 1С, но сторонняя система такой XML поймёт с трудом из-за служебных типов платформы.

Для интеграции с веб-сервисами и внешними приложениями дерево обычно преобразуют вручную: обходят строки рекурсивно и формируют массив структур, который затем записывают в JSON через ЗаписьJSON. Это даёт полный контроль над именами полей и структурой вложенности.

Функция ДеревоВМассив(Строки)

Результат = Новый Массив;

Для Каждого Строка Из Строки Цикл

Элемент = Новый Структура("Наименование,Код", Строка.Наименование, Строка.Код);

Элемент.Вставить("Подчиненные", ДеревоВМассив(Строка.Строки));

Результат.Добавить(Элемент);

КонецЦикла;

Возврат Результат;

КонецФункции

Типичные ошибки
01
Сохранять дерево через ЗначениеВСтрокуВнутр для обмена между разными базами — ссылки станут битыми
02
Забыть вызвать Получить() у ХранилищеЗначения и обращаться к хранилищу как к дереву
03
Объявить дерево локальной переменной формы и терять его при каждом серверном вызове
04
Записывать большие деревья без сжатия — таблица базы разрастается
05
Не проверять результат Получить() на Неопределено при пустом реквизите

Сохранение в файл на диске

Если дерево нужно передать пользователю или сохранить как внешнюю копию, данные записывают в файл. Два рабочих варианта: XML через сериализатор (сохраняет типы и иерархию точно) либо табличные форматы (CSV, MXL, Excel) — но они плоско представляют иерархию, и для восстановления дерева придётся кодировать уровни вручную, например отступами или служебной колонкой «Уровень».

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

Для резервного копирования настроек практичнее всего комбинация: ЗначениеВСтрокуВнутр() → запись строки в текстовый файл. Восстановление — обратное чтение файла и ЗначениеИзСтрокиВнутр() с обязательной проверкой результата в попытке-исключении, так как повреждённый файл вызовет ошибку разбора.

Дерево значений в управляемой форме

Чтобы дерево «жило» на форме между серверными вызовами, добавьте реквизит формы типа ДеревоЗначений и вложенные реквизиты-колонки. Платформа сама сериализует такой реквизит при передаче между клиентом и сервером — дополнительных действий не требуется.

Если дерево строится программно и колонки заранее неизвестны, их добавляют методом ИзменитьРеквизиты() формы. После этого колонки становятся доступны для привязки к элементам формы и сохраняются в течение жизни формы.

Для сохранения состояния дерева между сеансами (развёрнутые ветки, позиция курсора) используют хранилище настроек: ХранилищеОбщихНастроек.Сохранить() и ХранилищеОбщихНастроек.Загрузить(). Само дерево туда лучше класть через ЗначениеВСтрокуВнутр(), так как настройки сериализуются платформой и не всякий объект переживёт эту процедуру корректно.

Сравнение способов сохранения

ЗначениеВСтрокуВнутр

Быстро и компактно, но только внутри одной базы и закрытый формат

ХранилищеЗначения

Привязка к объекту базы, сжатие; не подходит для обмена с внешними системами

XML через СериализаторXDTO

Обмен между базами 1С с сохранением типов; громоздкий формат для сторонних систем

JSON через структуры

Интеграция с веб-сервисами; требует ручного обхода дерева

Реквизит формы

Хранение только в пределах открытой формы, без записи в базу

Проверка результата и ограничения

После восстановления дерева всегда проверяйте три вещи: результат не равен Неопределено, тип результата — именно ДеревоЗначений (ТипЗнч()), количество колонок и строк соответствует ожиданиям. Пустой реквизит хранилища возвращает Неопределено, и обращение к Строки у него вызовет ошибку.

Ограничения, о которых стоит помнить: очень большие деревья (десятки тысяч строк с десятками колонок) при сериализации дают ощутимый объём данных — сжатие обязательно. Ссылочные значения в колонках привязывают дерево к конкретной базе. Наконец, версия платформы имеет значение: строка, созданная ЗначениеВСтрокуВнутр() на новой платформе, может не прочитаться на старой.

Контрольные точки

Как проверить восстановление дерева

Результат Получить() или ЗначениеИзСтрокиВнутр
Норма

Тип ДеревоЗначений, строки и колонки на месте

Тревожный признак

Неопределено или ошибка разбора строки

Ссылочные значения в колонках
Норма

Ссылки открываются, запросы по ним работают

Тревожный признак

Пустые представления, ошибки «Объект не найден»

Иерархия после восстановления
Норма

Уровни вложенности совпадают с исходными

Тревожный признак

Все строки на одном уровне — потеряна структура

Частые вопросы

Можно ли сохранить дерево значений в константу?

Да, если константа имеет тип ХранилищеЗначения — дерево помещается через Новый ХранилищеЗначения(Дерево). Альтернатива — строковая константа неограниченной длины с результатом ЗначениеВСтрокуВнутр(). Для часто меняющихся данных константа не лучший выбор: её изменение требует монопольного доступа при определённых условиях и фиксируется в транзакции.

Почему после восстановления из строки дерево пустое?

Чаще всего строка была повреждена при передаче (обрезана, изменена кодировка) или сохранялся не тот объект. Проверьте длину строки до и после, а также оберните ЗначениеИзСтрокиВнутр() в Попытка/Исключение и проверьте тип результата через ТипЗнч().

Как передать дерево значений с сервера на клиент в управляемой форме?

Напрямую объект ДеревоЗначений между клиентом и сервером не передаётся. Используйте реквизит формы типа ДеревоЗначений — платформа передаёт его автоматически. Либо сериализуйте дерево в строку на сервере и восстановите на клиенте, если клиент толстый.

Сохраняются ли типы колонок при сериализации?

При использовании ЗначениеВСтрокуВнутр, ХранилищеЗначения и СериализатораXDTO типы колонок сохраняются полностью. При ручной выгрузке в JSON или CSV типы теряются — их нужно восстанавливать программно при загрузке, например пересоздавая колонки с нужным ОписаниеТипов.

Что делать, если дерево содержит картинки или двоичные данные?

Двоичные данные и картинки сериализуются штатными механизмами (ЗначениеВСтрокуВнутр, ХранилищеЗначения), но сильно увеличивают объём. Для больших файлов лучше хранить их отдельно, а в дереве держать только ссылки или идентификаторы.

Можно ли сохранить дерево значений в регистр сведений?

Да, через ресурс типа ХранилищеЗначения — механика та же, что и со справочником. Это удобно для хранения нескольких вариантов дерева по измерениям, например отдельное дерево настроек для каждого пользователя и каждого отчёта.

Итог

Что важно запомнить

Дерево значений — объект сеанса, в базу напрямую не пишется. Для хранения используйте ХранилищеЗначения или ЗначениеВСтрокуВнутр, для обмена — XML/JSON, для формы — реквизит типа ДеревоЗначений. После восстановления всегда проверяйте тип и структуру результата.