Введение
Общеизвестный факт, что для обменов можно использовать следующие способы:
- Хранение через формат xml. Поддерживается всеми версиями 1С 8.х практически для всех объектов. Разве что некоторое время назад
- Хранение через json. Более новый формат хранения данных в виде стоки.
- Хранение через внутренний формат данных. Формат использовался в 1С 7.7 и продолжает использоваться, хотя уже в меньшей степени для 1С 8.х.
Однако, для хранения данных пригодно не только хранение в этих форматах, но и сам тип ХранилищеЗначений. В этой статье детально рассмотрен алхимический способ работы с типом хранилище значений, чтобы получить внутренний формат на языке 1С даже на мобильных устройствах.
Краткие сведения о типе и работе с ним из Документации
Тип появился в платформе 1С 8.0, поддерживается на всей линейке 1С 8.х на компьютерных и мобильных устройствах, имеет основные методы:
- Получить() - для восстановления сохраненных данных.
- Конструктор новый ХранилищеЗначений(Значение).
Пункты 1 и 2 пригодны для типов данных, которые позволяют выполнить сериализацию данных плюс использование типа РезультатЗапроса, который тоже можно сохранить в Хранилище и потом успешно восстановить.
Но здесь ключевой момент с точки зрения алхимии разработки - сам тип способен конвертироваться в base64 строку, что позволяет проводить над самим типом и его значением нестандартные операции.
Хранилище значений - взгляд алхимика
Поскольку хранилище значений можно превратить в base64 строку, то это позволяет выгрузить его содержимое целиком в файл. Для этого можно использовать следующий код:
ТМП = ПолучитьИмяВременногоФайла("txt");
Тер = XMLЗначение(Тип("ДвоичныеДанные"),XMLСтрока(новый ХранилищеЗначения(ЗначениеКонвертации)));
Тер.Записать(ТМП);
Скриншот содержимого получившегося файла для хранилища без сжатия выглядит таким образом:

Получается, что хранилище значений вызывает встроенную функцию сериализации во внутренний формат 1С (подробнее про описание формата здесь)!!! А это означает, что во внутренний формат 1С можно выгрузить и загрузить данные целиком через тип ХранилищеЗначений, и так как сие типы поддерживаются и мобильной платформой, то можно реализовать работу с внутренним форматом 1С на мобильной платформе без внешних компонент!!!
Базовые функции и процедуры для реализации
Поскольку хранилище значений, имеет размер спецификации в 13 впереди стоящих символов и сохраненное ранее значение - это позволяет сделать следующие операции:
- Выгрузить пригодные к сериализации типы данных по технологии внутреннего формата данных.
- Восстановить значения из внутреннего формата 1С через предварительную конвертацию данных.
Любое значение во внутренний формат при помощи следующий функции:
функция _ЗначениеВСтрокуВнутрМобильник(ЗначениеКонвертации)
ТМП = ПолучитьИмяВременногоФайла("txt");
Строка1 = Преобразовать64(XMLСтрока(новый ХранилищеЗначения(ЗначениеКонвертации)));
// Вычислить спецификацию Хранилища Значений в формате XML без сжатия:
Размер=13;
Пока Размер>0 Цикл Строка1.Удалить(0); Размер=Размер-1; КонецЦикла;
//Теперь осталось это превратить в нормальный тип данных - самое тупое просто через
// двоичку конвертировать!!!
Тер = XMLЗначение(Тип("ДвоичныеДанные"),Преобразовать64(,Строка1));
Тер.Записать(ТМП);
//Достаем сохраненные данные из спецификации:
Текст = новый ЧтениеТекста(ТМП);
Содержимое=Текст.Прочитать();
Текст.Закрыть();
УдалитьФайлы(ТМП);
Возврат Содержимое;
КонецФункции
Данный способ позволяет неявно вызвать сериализацию внутреннего формата и таким образом легко сохранить нужные типы данных.
А вот восстановить значение из этого формата таким способом, гораздо сложнее, тем не менее возможно.
Алгортм следующий:
- Если тип простой - восстановливать через хранилище значений.
- Сложные коллекции и типы - разобрать до простых и поэтапно восстанавливать.
Для этого нужно предварительно загрузить в хранилище шаблон значения, а уже потом приделав к нему спецификацию 13 символов и скорректировать адрес обращения удастся восстановить значение.
Восстановить простое значение можно только через некоторое значение образец:
функция _ЗначениеИзСтрокиВнутрМобильник(ЗначениеОбразец,ЗначениеКонвертации)
//Все делаем но наоборот!!!
ТМП = ПолучитьИмяВременногоФайла("txt");
Текст = новый ЗаписьТекста(ТМП);
Текст.Записать(ЗначениеКонвертации);
Текст.Закрыть();
ОБ_ДД=Новый ДвоичныеДанные(ТМП);
МассивБезСпецификации = Преобразовать64(XMLСтрока(ОБ_ДД));
//Образец для генерации спецификации хранилища значений!!! - без неё исходное не восстановить!!!
ШаблонХранилища_XML= XMLСтрока(новый ХранилищеЗначения(ЗначениеОбразец));
МассивСпецификации = Преобразовать64(ШаблонХранилища_XML);
Размер=13;
Ид=0;
Пока Ид<Размер Цикл МассивБезСпецификации.Вставить(Ид,МассивСпецификации[Ид]); Ид=Ид+1; КонецЦикла;
//Теперь в Хранилище значения и достаем!!!
XML_СтруктураТипа = Преобразовать64(,МассивБезСпецификации);
//Поскольку парсер иногда косячит - сдвигает лишние плсюы и прочее - нужно сделать следующее:
//7v3 - это начало значения поиска!!!
Дескриптор = Лев(ШаблонХранилища_XML,Найти( ШаблонХранилища_XML,"7v3")-1);
XML_СтруктураТипа = Дескриптор+Сред(XML_СтруктураТипа, Найти( XML_СтруктураТипа,"7v3"));
Хранилище=XMLЗначение(Тип("ХранилищеЗначения"),XML_СтруктураТипа);
УдалитьФайлы(ТМП);
возврат Хранилище.Получить();
КонецФункции
Предостережение:
Если допустить ошибку с шаблоном значения при восстановлении через хранилище, то 1С потом не сможет верно преобразовать значение, и получите сообщение о ошибке формата потока в лучшем случае, в худшем случае придется перезапустить сеанс.
На практике поэтому рекомендация:
- Восстанавливать значения таким образом: сначала восстанавливается тип, а потом проверяется - имеются ли в системе методы его обработки.
- Если тип не простой (коллекция, структура) и нет возможности восстановить элемент - лучше вернуть Неопределено и сохранить работоспособность, предварительно выдав уведомление что такой тип невозможно восстановить.
Для тестирования этих функций используется следующий код:
СсылкаКонец = Справочники.Банки.ПолучитьСсылку(новый УникальныйИдентификатор);
СсылочныйТип=ЭтотОбъект.Метаданные().Реквизиты.ТипЛюбаяСсылка.Тип.Типы();
Если СсылочныйТип.Количество()=0 или СсылочныйТип.Количество()=1 Тогда
Сообщить("Нет ссылок для тестирования!!!");
Возврат;
КонецЕсли;
СсылкаКонец = новый(СсылочныйТип[1]);
Сообщить("--Тест алгоритма в строку--");
Тест1 = ЗначениеВСтрокуВнутр(СсылкаКонец);
Содержимое=_ЗначениеВСтрокуВнутрМобильник(СсылкаКонец);
Сообщить(Тест1);
Сообщить(Содержимое);
Сообщить(Содержимое=Тест1);
Сообщить("--Тест алгоритма из строки--");
Тест1 = ЗначениеИзСтрокиВнутр(Содержимое);
//Тестирование типов данных:
Содержимое1=_ЗначениеИзСтрокиВнутрМобильник(ТипЗнч(Тест1),"{""T"",51e7a0d2-530b-11d4-b98a-008048da3034}");
Содержимое=_ЗначениеИзСтрокиВнутрМобильник(СсылкаКонец,Содержимое);
Сообщить(Тест1);
Сообщить(Содержимое1);
Сообщить(Содержимое);
Сообщить(Содержимое=Тест1);
Постскриптум:
Используемые методы позволяют на 90% перенести функционал работы с внутренним форматом на мобильную платформу, и тем самым улучшить совместимость прикладных решений на мобильных устройствах.
Отдельная благодарность автору функции Преобразовать64 благодаря которой , можно решать не один класс задач на такие строки.
Для сохранения целостности изложения результатов, просто продублирую функцию из публикации.
&НаСервере
Функция Преобразовать64(Строка64 = неопределено, Массив64 = неопределено)
Таб64 = Новый ТаблицаЗначений;
Таб64.Колонки.Добавить("Код");
Таб64.Колонки.Добавить("Символ");
Нпп = 0;
Для Код = КодСимвола("A") По КодСимвола("Z") Цикл
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = Символ(Код);
Нпп = Нпп + 1;
КонецЦикла;
Для Код = КодСимвола("a") По КодСимвола("z") Цикл
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = Символ(Код);
Нпп = Нпп + 1;
КонецЦикла;
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = "0";
Нпп = Нпп + 1;
Для Код = 1 По 9 Цикл
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = Формат(Код,"ЧЦ=1; ЧДЦ=0");
Нпп = Нпп + 1;
КонецЦикла;
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = "+";
Нпп = Нпп + 1;
стр = Таб64.Добавить();
стр.Код = Нпп;
стр.Символ = "/";
Если Массив64 = неопределено Тогда
Если Строка64 = неопределено Тогда
Сообщить("неверный вызов функции");
Возврат неопределено;
КонецЕсли;
//Уберем переносы строк
Строка64 = СтрЗаменить(Строка64, Символ(10), "");
Строка64 = СтрЗаменить(Строка64, Символ(13), "");
//преобразовываем строку в массив
Если СтрДлина(Строка64) % 4 <> 0 Тогда
Сообщить("длина строки на входе должна быть кратна 4");
Сообщить(СтрДлина(Строка64));
Возврат неопределено;
КонецЕсли;
Кол4 = Цел(СтрДлина(Строка64) / 4);
РазмерМ = Кол4 * 3;
Если Прав(Строка64, 2) = "==" Тогда
РазмерМ = РазмерМ - 2;
ИначеЕсли Прав(Строка64, 1) = "=" Тогда
РазмерМ = РазмерМ - 1;
КонецЕсли;
Массив64 = Новый Массив(РазмерМ);
Для А = 1 По Кол4 Цикл
Число3 = 0;
Для Б = 1 По 4 Цикл
Буква1 = Сред(Строка64, (А-1)*4 + Б, 1);
Если Буква1 = "=" Тогда
Код4 = 0;
Иначе
стрН = Таб64.Найти(Буква1, "Символ");
Если стрН = Неопределено Тогда
Сообщить("ошибка при поиске "+ КодСимвола(Буква1));
Иначе
Код4 = стрН.Код;
КонецЕсли;
КонецЕсли;
Число3 = Число3 * 64 + Код4;
КонецЦикла;
Ост = Число3 % 256;
Индекс = (А-1)*3 + 2;
Если Индекс <= РазмерМ - 1 Тогда
Массив64[Индекс] = Ост;
КонецЕсли;
Число3 = (Число3 - Ост) / 256;
Ост = Число3 % 256;
Индекс = (А-1)*3 + 1;
Если Индекс <= РазмерМ - 1 Тогда
Массив64[Индекс] = Ост;
КонецЕсли;
Число3 = (Число3 - Ост) / 256;
Ост = Число3 % 256;
Массив64[(А-1)*3 + 0] = Ост;
КонецЦикла;
Возврат Массив64;
Иначе //Преобразовываем массив в строку
Строка64 = "";
Кол3 = Цел(Массив64.Количество() / 3);
Если Массив64.Количество() % 3 <> 0 Тогда
Кол3 = Кол3 + 1;
КонецЕсли;
Для А = 1 По Кол3 Цикл
Число3 = Массив64[(А-1)*3];
Если (А-1)*3+1 <= Массив64.ВГраница() Тогда
Код3 = Массив64[(А-1)*3+1];
Иначе
Код3 = 0;
КонецЕсли;
Число3 = Число3*256 + Код3;
Если (А-1)*3+2 <= Массив64.ВГраница() Тогда
Код3 = Массив64[(А-1)*3+2];
Иначе
Код3 = 0;
КонецЕсли;
Число3 = Число3*256 + Код3;
Ост4 = Число3 % 64;
Число3 = (Число3 - Ост4) / 64;
Ост3 = Число3 % 64;
Число3 = (Число3 - Ост3) / 64;
Ост2 = Число3 % 64;
Число3 = (Число3 - Ост2) / 64;
Ост1 = Число3 % 64;
Число3 = (Число3 - Ост1) / 64;
стрН = Таб64.Найти(Ост1, "Код");
Если стрН = неопределено Тогда
Сообщить("Ошибка при поиске");
Иначе
Строка64 = Строка64 + стрН.Символ;
КонецЕсли;
стрН = Таб64.Найти(Ост2, "Код");
Если стрН = неопределено Тогда
Сообщить("Ошибка при поиске");
Иначе
Строка64 = Строка64 + стрН.Символ;
КонецЕсли;
стрН = Таб64.Найти(Ост3, "Код");
Если стрН = неопределено Тогда
Сообщить("Ошибка при поиске");
Иначе
Строка64 = Строка64 + стрН.Символ;
КонецЕсли;
стрН = Таб64.Найти(Ост4, "Код");
Если стрН = неопределено Тогда
Сообщить("Ошибка при поиске");
Иначе
Строка64 = Строка64 + стрН.Символ;
КонецЕсли;
КонецЦикла;
//Если количество символов не делится на 3, то на конце должны быть
//знаки "="
Если Массив64.Количество() % 3 = 1 Тогда
Строка64 = Лев(Строка64, СтрДлина(Строка64)-2)+ "==";
ИначеЕсли Массив64.Количество() % 3 = 2 Тогда
Строка64 = Лев(Строка64, СтрДлина(Строка64)-1)+ "=";
КонецЕсли;
Возврат Строка64;
КонецЕсли;
Вступайте в нашу телеграмм-группу Инфостарт