Введение
Общеизвестный факт, что для обменов можно использовать следующие способы:
- Хранение через формат 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;
КонецЕсли;
КонецФункции
Обновление от 28.08.2026
В связи с тем, что вышло обновление библиотеки двоичных данных - больше нет необходимости работать через файлы (кодировка хранилища и базы - UTF-8) - код стал лаконичнее:
функция _ЗначениеИзСтрокиВнутрМобильник(ЗначениеОбразец,ЗначениеКонвертации)
//Все делаем но наоборот!!!
МассивБезСпецификации = ПолучитьМассивБайтовИзСтроки(ЗначениеКонвертации);
//Образец для генерации спецификации хранилища значений!!! - без неё исходное не восстановить!!!
ШаблонХранилища_XML= XMLСтрока(новый ХранилищеЗначения(ЗначениеОбразец));
МассивСпецификации = Преобразовать64(ШаблонХранилища_XML);
Размер=13;
Ид=0;
Пока Ид<Размер Цикл МассивБезСпецификации.Вставить(Ид,МассивСпецификации[Ид]); Ид=Ид+1; КонецЦикла;
//Теперь в Хранилище значения и достаем!!!
XML_СтруктураТипа = Преобразовать64(,МассивБезСпецификации);
//Поскольку парсер иногда косячит - сдвигает лишние плсюы и прочее - нужно сделать следующее:
//7v3 - это начало значения поиска!!!
Дескриптор = Лев(ШаблонХранилища_XML,Найти( ШаблонХранилища_XML,"7v3")-1);
XML_СтруктураТипа = Дескриптор+Сред(XML_СтруктураТипа, Найти( XML_СтруктураТипа,"7v3"));
Хранилище=XMLЗначение(Тип("ХранилищеЗначения"),XML_СтруктураТипа);
возврат Хранилище.Получить();
КонецФункции
функция _ЗначениеВСтрокуВнутрМобильник(ЗначениеКонвертации)
//ТМП = ПолучитьИмяВременногоФайла("txt");
Строка1 = Преобразовать64(XMLСтрока(новый ХранилищеЗначения(ЗначениеКонвертации)));
// Вычислить спецификацию Хранилища Значений в формате XML без сжатия:
Размер=13;
Пока Размер>0 Цикл Строка1.Удалить(0); Размер=Размер-1; КонецЦикла;
//Теперь осталось это превратить в нормальный тип данных - через библиотеку двоичных данных и строки UTF-8:
Содержимое=ПолучитьСтрокуИзМассиваБайтов(Строка1);
Возврат Содержимое;
КонецФункции
Полная версия библиотеки - здесь
В публикации были использованы следующие функции библиотеки двоичных данных:
// Обратная функция - преобразование массива байтов в строку
Функция ПолучитьСтрокуИзМассиваБайтов(МассивБайтов, Кодировка = "UTF8")
Если Кодировка = "UTF16" Тогда
// UTF-16 Little Endian
ДлинаСтроки = Цел(МассивБайтов.Количество() / 2);
Строка = "";
Для i = 0 По ДлинаСтроки - 1 Цикл
МладшийБайт = МассивБайтов[i*2];
СтаршийБайт = МассивБайтов[i*2 + 1];
Код = СтаршийБайт * 256 + МладшийБайт;
Строка = Строка + Символ(Код);
КонецЦикла;
ИначеЕсли Кодировка = "UTF8" Тогда
// Декодирование UTF-8 (упрощенная версия для базового набора символов)
Строка = "";
i = 0;
Пока i < МассивБайтов.Количество() Цикл
Байт1 = МассивБайтов[i];
Если Байт1 <= 127 Тогда
// 1 байт
Строка = Строка + Символ(Байт1);
i = i + 1;
ИначеЕсли Байт1 >= 192 И Байт1 <= 223 Тогда
// 2 байта
Если i + 1 >= МассивБайтов.Количество() Тогда
ВызватьИсключение "Неверная UTF-8 последовательность";
КонецЕсли;
Байт2 = МассивБайтов[i+1];
Код = (Байт1 - 192) * 64 + (Байт2 - 128);
Строка = Строка + Символ(Код);
i = i + 2;
ИначеЕсли Байт1 >= 224 И Байт1 <= 239 Тогда
// 3 байта
Если i + 2 >= МассивБайтов.Количество() Тогда
ВызватьИсключение "Неверная UTF-8 последовательность";
КонецЕсли;
Байт2 = МассивБайтов[i+1];
Байт3 = МассивБайтов[i+2];
Код = (Байт1 - 224) * 4096 + (Байт2 - 128) * 64 + (Байт3 - 128);
Строка = Строка + Символ(Код);
i = i + 3;
Иначе
ВызватьИсключение "Неверный байт в UTF-8 последовательности: " + Байт1;
КонецЕсли;
КонецЦикла;
ИначеЕсли Кодировка = "ANSI" Тогда
// ANSI - просто преобразуем байты в символы
Строка = "";
Для i = 0 По МассивБайтов.Количество() - 1 Цикл
Строка = Строка + Символ(МассивБайтов[i]);
КонецЦикла;
Иначе
ВызватьИсключение "Неизвестная кодировка: " + Кодировка;
КонецЕсли;
Возврат Строка;
КонецФункции
Функция ПолучитьМассивБайтовИзСтроки(Строка, Кодировка = "UTF8")
ДлинаСтроки = СтрДлина(Строка);
Если Кодировка = "UTF16" Тогда
// UTF-16 Little Endian (как в оригинальной функции)
МассивБайтов = Новый Массив(ДлинаСтроки * 2);
Для i = 1 По ДлинаСтроки Цикл
Символ = Сред(Строка, i, 1);
Код = КодСимвола(Символ);
// Разбиваем код на два байта (little-endian)
МассивБайтов[(i-1)*2] = Код % 256; // Младший байт
МассивБайтов[(i-1)*2 + 1] = Цел(Код / 256); // Старший байт
КонецЦикла;
ИначеЕсли Кодировка = "UTF8" Тогда
// UTF-8 кодировка
МассивБайтов = Новый Массив;
Для i = 1 По ДлинаСтроки Цикл
Символ = Сред(Строка, i, 1);
Код = КодСимвола(Символ);
// Кодируем символ в UTF-8
Если Код <= 127 Тогда
// 1 байт: 0xxxxxxx
МассивБайтов.Добавить(Код);
ИначеЕсли Код <= 2047 Тогда
// 2 байта: 110xxxxx 10xxxxxx
МассивБайтов.Добавить(192 + Цел(Код / 64)); // 192 = 0xC0
МассивБайтов.Добавить(128 + (Код % 64)); // 128 = 0x80
Иначе
// 3 байта: 1110xxxx 10xxxxxx 10xxxxxx
МассивБайтов.Добавить(224 + Цел(Код / 4096)); // 224 = 0xE0
МассивБайтов.Добавить(128 + Цел((Код % 4096) / 64));
МассивБайтов.Добавить(128 + (Код % 64));
КонецЕсли;
КонецЦикла;
ИначеЕсли Кодировка = "ANSI" Тогда
// ANSI (однобайтовая кодировка) - просто коды символов
МассивБайтов = Новый Массив(ДлинаСтроки);
Для i = 1 По ДлинаСтроки Цикл
Символ = Сред(Строка, i, 1);
Код = КодСимвола(Символ);
// Для ANSI используем только младший байт
МассивБайтов[i-1] = Код % 256;
КонецЦикла;
Иначе
ВызватьИсключение "Неизвестная кодировка: " + Кодировка;
КонецЕсли;
Возврат МассивБайтов;
КонецФункции
Проверено на следующих конфигурациях и релизах:
- Управление нашей фирмой, редакция 1.6, релизы 1.6.27.222
- Управление торговлей, редакция 10.3, релизы 10.2.8.2
Вступайте в нашу телеграмм-группу Инфостарт