При работе с платформой 1С:Предприятие 8 мы часто сталкиваемся с необходимостью хранить и передавать сложные типы данных, такие как таблицы значений, структуры, картинки или даже целые объекты. Для этих целей платформа предоставляет мощный механизм — объект ХранилищеЗначения. Однако, иногда возникает вопрос, как можно сериализовать (преобразовать в строку для хранения или передачи) и десериализовать (восстановить из строки) содержимое этого хранилища, особенно когда оно содержит сложные типы данных, используя стандартные XML-методы.
Давайте вместе разберемся в этом интересном кейсе, рассмотрим внутренние механизмы и выясним, как правильно использовать методы XML-сериализации для работы с ХранилищеЗначения.
Прежде чем перейти к решению, рассмотрим подробнее, что представляет собой объект ХранилищеЗначения в 1С:Предприятии.
ХранилищеЗначения — это специальный тип данных, предназначенный для хранения любых сериализуемых значений в сжатом виде. Оно позволяет нам:
ХранилищеЗначения может содержать практически любой сериализуемый объект 1С, будь то ТаблицаЗначений, Структура, Массив, ДвоичныеДанные, или даже сложный объект метаданных.ХранилищеЗначения использует 32-битный алгоритм Deflate для сжатия хранимой информации. Это особенно полезно для больших объектов, таких как изображения или файлы.ХранилищеЗначения хранится как бинарный объект (VARBINARY(MAX) или bytea), и СУБД не знает о его внутреннем содержимом. Это означает, что по полям типа ХранилищеЗначения нельзя выполнять индексирование, упорядочивание или обеспечивать ссылочную целостность.Мы должны помнить, что прямое использование ХранилищеЗначения для очень больших объемов информации в полях объектов может замедлить работу системы. Рекомендуется использовать его для данных, действительно необходимых в работе, а не для хранения файлового архива.
Платформа 1С:Предприятие предлагает несколько способов для сериализации и десериализации данных:
ЗначениеВФайл() и ЗначениеИзФайла() позволяют сохранять и восстанавливать значения в/из файлов.СохранитьЗначение() и ВосстановитьЗначение() используются для сохранения и восстановления параметров пользователя между сеансами.ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр() преобразуют значения в специальный строковый формат. Фирма 1С не рекомендует использовать их для общих целей, кроме обмена с 1С:Предприятием 7.7, но они дают нам представление о внутреннем устройстве платформы. Интересно, что ХранилищеЗначения, как мы выясним, внутренне оперирует с данными в формате, схожем с ЗначениеВСтрокуВнутр().XMLСтрока() и XMLЗначение() предназначены для работы с XML-представлениями значений. Традиционно они используются для обмена данными между различными информационными базами или с внешними системами. Важный момент: обычно эти методы ожидают простые типы данных. Передача сложных типов напрямую часто приводит к исключению. Однако, с ХранилищеЗначения ситуация обстоит иначе.Рассмотрим основной вопрос: как сериализовать объект, помещенный в ХранилищеЗначения, в XML-строку и затем восстановить его. Мы увидим, что, несмотря на кажущуюся сложность, механизм работает достаточно прозрачно.
Представим, что у нас есть ТаблицаЗначений, которую мы хотим сохранить в ХранилищеЗначения, а затем это хранилище сериализовать в XML-строку.
Сначала нам нужно создать какую-либо сложную структуру данных, например, ТаблицуЗначений, и поместить ее в ХранилищеЗначения. Давайте создадим простую таблицу и заполним ее несколькими строками.
// Создаем ТаблицуЗначений
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Имя");
ТЗ.Колонки.Добавить("Возраст");
// Заполняем ТаблицуЗначений
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Имя = "Иван";
НоваяСтрока.Возраст = 30;
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Имя = "Мария";
НоваяСтрока.Возраст = 25;
// Помещаем ТаблицуЗначений в ХранилищеЗначения
Хранилище = Новый ХранилищеЗначения(ТЗ);
На этом этапе объект Хранилище содержит нашу ТаблицуЗначений в сериализованном и, возможно, сжатом виде.
Теперь, когда у нас есть объект ХранилищеЗначения, мы можем преобразовать его в XML-строку. Здесь кроется ключевой момент, который обсуждался на форуме: хотя XMLСтрока() обычно не работает со сложными типами, она прекрасно работает с объектом ХранилищеЗначения. Почему? Потому что сам объект ХранилищеЗначения с точки зрения XML-сериализации является "простым" контейнером, а его содержимое (та же ТаблицаЗначений) уже сериализовано внутри него. Платформа 1С знает, как представить ХранилищеЗначения в XML.
// Сериализуем ХранилищеЗначения в XML-строку
СтрокаXML = XMLСтрока(Хранилище);
// Выведем полученную строку для наглядности (в реальных условиях ее можно сохранить или передать)
Сообщить(СтрокаXML);
Если мы посмотрим на содержимое СтрокаXML, то увидим XML-представление объекта ХранилищеЗначения. Оно будет содержать данные, закодированные в формате Base64, что является стандартным способом представления бинарных данных в XML. Типу ХранилищеЗначения в XML соответствует тип данных ValueStorage из пространства имен http://v8.1c.ru/data.
Для восстановления объекта ХранилищеЗначения из XML-строки мы используем метод XMLЗначение(). Очень важно указать правильный тип данных, в который мы хотим десериализовать строку. В нашем случае это Тип("ХранилищеЗначения").
// Десериализуем XML-строку обратно в ХранилищеЗначения
ВосстановленноеХранилище = XMLЗначение(Тип("ХранилищеЗначения"), СтрокаXML);
// Проверим, что это действительно ХранилищеЗначения
Сообщить("Тип восстановленного объекта: " + ТипЗнч(ВосстановленноеХранилище));
На этом этапе у нас есть объект ХранилищеЗначения, который идентичен исходному. Но нам нужны сами данные, которые мы в него помещали (наша ТаблицаЗначений).
Чтобы получить ТаблицуЗначений из восстановленного ХранилищеЗначения, мы используем его метод Получить().
// Извлекаем ТаблицуЗначений из восстановленного ХранилищаЗначения
ВосстановленнаяТЗ = ВосстановленноеХранилище.Получить();
// Проверяем тип и содержимое восстановленной ТаблицыЗначений
Сообщить("Тип извлеченного объекта: " + ТипЗнч(ВосстановленнаяТЗ));
Для Каждого СтрокаТЗ Из ВосстановленнаяТЗ Цикл
Сообщить("Имя: " + СтрокаТЗ.Имя + ", Возраст: " + СтрокаТЗ.Возраст);
КонецЦикла;
Как мы видим, исходная ТаблицаЗначений была успешно восстановлена со всеми данными. Этот подход, предложенный одним из участников форума, демонстрирует, что комбинация ХранилищеЗначения с XML-методами является вполне рабочим и корректным решением для сериализации сложных данных.
Мы проанализируем ситуацию, чтобы понять, почему этот "кейс" оказался таким интересным:
ХранилищеЗначения — это контейнер: ХранилищеЗначения само по себе является объектом платформы, который спроектирован для сериализации своего содержимого. Когда мы передаем ТаблицуЗначений в конструктор Новый ХранилищеЗначения(ТЗ), платформа внутренне сериализует ТЗ (используя механизм, схожий с ЗначениеВСтрокуВнутр()) и, возможно, сжимает ее.ХранилищаЗначения: Метод XMLСтрока() умеет работать с объектом ХранилищеЗначения. Он не пытается "заглянуть" внутрь и сериализовать ТаблицуЗначений напрямую как XML (что привело бы к ошибке), а сериализует сам объект ХранилищеЗначения. При этом внутреннее бинарное представление данных ХранилищаЗначения кодируется в Base64 и помещается в XML-строку.XMLЗначение(Тип("ХранилищеЗначения"), СтрокаXML) восстанавливает объект ХранилищеЗначения из Base64-кодированной строки. Затем метод Получить() объекта ХранилищеЗначения десериализует внутреннее представление данных обратно в исходный объект (ТаблицуЗначений).Таким образом, мы используем ХранилищеЗначения как "черный ящик", который умеет самостоятельно сериализовать и десериализовать свое содержимое, а XML-методы лишь упаковывают этот "черный ящик" в XML-строку.
Хотя этот подход эффективен, мы должны учитывать некоторые нюансы:
ХранилищеЗначения. Например, некоторые служебные объекты СКД (компоновщик настроек) могут быть несериализуемыми. При попытке сохранить такой объект будет выдано исключение.ХранилищаЗначения может быть ресурсоемким. Если нам нужно часто проверять заполненность хранилища без извлечения данных, рекомендуется использовать дополнительный флаг.ХранилищеЗначения недоступен напрямую в управляемой форме. Для работы с ним требуется использовать специальные подходы, например, через временные хранилища на сервере.ХранилищеЗначения, его содержимое не копируется автоматически. Мы должны будем копировать его программно.В заключение, мы выяснили, что сериализация и десериализация сложных типов данных через ХранилищеЗначения с использованием методов XMLСтрока() и XMLЗначение() является надежным и поддерживаемым платформой способом. Это позволяет нам эффективно обмениваться сложными данными между системами или сохранять их в базе данных в удобном и сжатом виде.