Как правильно распарсить массив JSON в 1С 7.7 с помощью MSScriptControl?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При интеграции устаревших систем на платформе 1С:Предприятие 7.7 с современными веб-сервисами разработчики неизбежно сталкиваются с необходимостью разбора (парсинга) входящих данных в формате JSON. Поскольку встроенных средств для работы с этим форматом в 1С 7.7 нет, стандартным подходом стало использование системного компонента MSScriptControl.ScriptControl с подключением движка JScript. Однако при попытке прочитать массив из полученной структуры данных разработчик получает ошибку. Вызов вида Инф.cars[0] завершается аварийно. Для отправки запросов к веб-сервисам и REST API можно использовать внешнюю компоненту HTTP(S)-клиента для 1С 7.7.

В этой статье мы подробно разберем, почему возникает эта ошибка, как обойти ограничения OLE-взаимодействия и как написать полноценный, быстрый и отказоустойчивый парсер JSON для вашей конфигурации 1С 7.7.

Выясняем причину: почему возникает ошибка?

Рассмотрим природу проблемы подробнее. Когда мы передаем JSON-строку в метод Eval или Run компонента ScriptControl, JScript парсит ее и возвращает в 1С ссылку на объект в своей внутренней памяти. В отладчике 1С этот объект отображается как тип OLE.JScriptTypeInfo. С простыми свойствами (строки, числа, булевы значения) платформа 1С 7.7 работает напрямую без проблем: выражение Инф.name вернет значение свойства "name".

Однако синтаксис языка 1С 7.7 принципиально не поддерживает работу с квадратными скобками [] для обращения к элементам коллекций внешних COM/OLE-объектов. Попытка написать Инф.cars[0] вызывает синтаксическую ошибку на стороне 1С, так как платформа пытается интерпретировать квадратные скобки по своим внутренним правилам (например, как обращение к реквизиту или элементу диалога), а не передает этот вызов OLE-серверу.

Решение проблемы: создание JScript-мостов

Чтобы обойти данное ограничение платформы, нам необходимо реализовать вспомогательные функции на стороне самого JScript. Поскольку 1С умеет передавать OLE-объекты обратно в методы ScriptControl как параметры, мы можем написать простые функции-обертки («мосты»), выполняющие нужные операции внутри среды JScript и возвращающие результат в 1С.

Давайте посмотрим на пример реализации такого скрипта-инициализатора. Мы добавим в наш экземпляр ScriptControl функции для определения типа данных, а также для получения элементов массивов и свойств ассоциативных объектов.


Процедура ИнициализироватьСкрипт(scriptCtrl)
    scriptCtrl.Language = "jscript";
    
    // Внедряем вспомогательные функции JScript для безопасной работы из OLE
    scriptCtrl.AddCode(
    "function parseJSON(strJSON) {
    |    return JSON.parse(strJSON);
    |}
    |function eType(obj) {
    |    if (obj === null) return 'null';
    |    if (obj === undefined) return 'undefined';
    |    if (Object.prototype.toString.call(obj) === '[object Array]') return 'array';
    |    return typeof obj;
    |}
    |function aGet(arr, index) {
    |    return arr[index];
    |}
    |function oSize(obj) {
    |    var size = 0, key;
    |    for (key in obj) {
    |        if (obj.hasOwnProperty(key)) size++;
    |    }
    |    return size;
    |}
    |function oKey(obj, index) {
    |    var size = 0, key;
    |    for (key in obj) {
    |        if (obj.hasOwnProperty(key)) {
    |            if (size == index) return key;
    |            size++;
    |        }
    |    }
    |    return '';
    |}
    |function oValue(obj, key) {
    |    return obj[key];
    |}"
    );
КонецПроцедуры

Разберем назначение этих функций подробнее:

  1. parseJSON(strJSON): использует встроенный и быстрый метод JSON.parse для преобразования строки в объект JScript.
  2. eType(obj): определяет тип переданного OLE-объекта (массив, объект, строка, число и т.д.), что критически важно для рекурсивного обхода дерева данных.
  3. aGet(arr, index): ключевая функция решения! Она принимает массив JScript и индекс, возвращая элемент массива обратно в 1С.
  4. oSize(obj), oKey(obj, index), oValue(obj, key): набор методов для обхода свойств объектов (ассоциативных массивов), позволяющий перебирать ключи и значения по индексу, подобно работе с объектом "Соответствие" в 1С 8.

Рекурсивный обход и конвертация JSON в СписокЗначений 1С

Теперь, когда у нас есть надежный мост для работы со структурами JScript, мы можем написать рекурсивную функцию на стороне 1С 7.7. Эта функция будет полностью обходить структуру JSON-объекта и преобразовывать ее в стандартные объекты 1С — СписокЗначений. Это позволит нам в дальнейшем легко обращаться к любым элементам данных с использованием привычного синтаксиса.

Проанализируем пример кода рекурсивного парсера:


Функция КонвертироватьВСписок(scriptCtrl, JSОбъект)
    Перем ТипОбъекта, РезСписок, Инд, Ключ, Значение, ЭлемТип, РазмерМассива, РазмерОбъекта;
    
    ТипОбъекта = scriptCtrl.Run("eType", JSОбъект);
    
    Если (ТипОбъекта = "string") или (ТипОбъекта = "number") или (ТипОбъекта = "boolean") Тогда
        Возврат JSОбъект;
    ИначеЕсли ТипОбъекта = "array" Тогда
        РезСписок = СоздатьОбъект("СписокЗначений");
        РазмерМассива = JSОбъект.length;
        Для Инд = 0 По РазмерМассива - 1 Цикл
            Значение = scriptCtrl.Run("aGet", JSОбъект, Инд);
            ЭлемТип = scriptCtrl.Run("eType", Значение);
            Если (ЭлемТип = "object") или (ЭлемТип = "array") Тогда
                РезСписок.ДобавитьЗначение(КонвертироватьВСписок(scriptCtrl, Значение));
            Иначе
                РезСписок.ДобавитьЗначение(Значение);
            КонецЕсли;
        КонецЦикла;
        Возврат РезСписок;
    ИначеЕсли ТипОбъекта = "object" Тогда
        РезСписок = СоздатьОбъект("СписокЗначений");
        РазмерОбъекта = scriptCtrl.Run("oSize", JSОбъект);
        Для Инд = 0 По РазмерОбъекта - 1 Цикл
            Ключ = scriptCtrl.Run("oKey", JSОбъект, Инд);
            Значение = scriptCtrl.Run("oValue", JSОбъект, Ключ);
            ЭлемТип = scriptCtrl.Run("eType", Значение);
            Если (ЭлемТип = "object") или (ЭлемТип = "array") Тогда
                РезСписок.Установить(Ключ, КонвертироватьВСписок(scriptCtrl, Значение));
            Иначе
                РезСписок.Установить(Ключ, Значение);
            КонецЕсли;
        КонецЦикла;
        Возврат РезСписок;
    Иначе
        Возврат ""; // Для null, undefined и неопределенных типов возвращаем пустую строку
    КонецЕсли;
КонецФункции

Теперь посмотрим, как просто и красиво выглядит основной исполняемый код разбора JSON в 1С с использованием нашей функции:


Процедура ВыполнитьРазборJSON()
    Перем scriptCtrl, ИсходнаяСтрока, JSONОбъект, Данные1С, МассивМашин, ПерваяМашина, ВтораяМашина, Имя, Возраст;
    
    scriptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
    ИнициализироватьСкрипт(scriptCtrl);
    
    // Исходные JSON-данные с массивом
    ИсходнаяСтрока = "{""name"":""John"",""age"":30,""cars"":[""Ford"",""BMW"",""Fiat""]}";
    
    // Парсим строку в объект JScript
    JSONОбъект = scriptCtrl.Run("parseJSON", ИсходнаяСтрока);
    
    // Конвертируем дерево JScript в иерархический СписокЗначений 1С
    Данные1С = КонвертироватьВСписок(scriptCtrl, JSONОбъект);
    
    // Получаем простые значения
    Имя = Данные1С.Получить("name"); // "John"
    Возраст = Данные1С.Получить("age"); // 30
    
    // Работаем с массивом напрямую через СписокЗначений!
    МассивМашин = Данные1С.Получить("cars");
    
    // Вытаскиваем элементы массива по индексу
    ПерваяМашина = МассивМашин.ПолучитьЗначение(1); // В 1С 7.7 индексация СпискаЗначений начинается с 1!
    ВтораяМашина = МассивМашин.ПолучитьЗначение(2);
    
    Сообщить("Имя: " + Имя + ", Возраст: " + Возраст);
    Сообщить("Первая машина в списке: " + ПерваяМашина); // Выведет "Ford"
    Сообщить("Вторая машина в списке: " + ВтораяМашина); // Выведет "BMW"
КонецПроцедуры

Альтернативное решение на современных ОС: замена `MSScriptControl` на `htmlfile`

Компонент MSScriptControl.ScriptControl является устаревшим 32-разрядным ActiveX-элементом. В современных версиях Windows (начиная с Windows 10/11 и Windows Server 2016/2019) он по умолчанию отключен из соображений безопасности или вовсе отсутствует в системе, что может вызывать ошибки при запуске вашей обработки 1С 7.7 у конечных пользователей.

Более надежной, современной и стабильной альтернативой является использование стандартного объекта htmlfile, который присутствует в любой версии Windows «из коробки» и работает без необходимости регистрации дополнительных библиотек в реестре. Рассмотрим пример инициализации парсера через htmlfile:


Функция ПолучитьПарсерHTML()
    Перем HTML, scriptCtrl;
    HTML = СоздатьОбъект("htmlfile");
    // Принудительно включаем режим совместимости с Internet Explorer 9 и выше для поддержки JSON.parse
    HTML.write("<meta http-equiv='x-ua-compatible' content='IE=9' />");
    
    // Получаем доступ к глобальному объекту window, выполняющему роль ScriptControl
    scriptCtrl = HTML.parentWindow;
    
    // Добавляем наши функции-мосты в глобальную область видимости документа
    scriptCtrl.execScript(
    "function eType(obj) {
    |    if (obj === null) return 'null';
    |    if (obj === undefined) return 'undefined';
    |    if (Object.prototype.toString.call(obj) === '[object Array]') return 'array';
    |    return typeof obj;
    |}
    |function aGet(arr, index) { return arr[index]; }
    |function oSize(obj) {
    |    var size = 0, key;
    |    for (key in obj) { if (obj.hasOwnProperty(key)) size++; }
    |    return size;
    |}
    |function oKey(obj, index) {
    |    var size = 0, key;
    |    for (key in obj) { if (obj.hasOwnProperty(key)) { if (size == index) return key; size++; } }
    |    return '';
    |}
    |function oValue(obj, key) { return obj[key]; }"
    );
    
    Возврат scriptCtrl;
КонецФункции

При использовании htmlfile вам больше не нужен отдельный метод parseJSON в коде скрипта, так как объект JSON поддерживается движком по умолчанию. Вы можете вызывать парсинг прямо из 1С следующим образом:


Парсер = ПолучитьПарсерHTML();
JSONОбъект = Парсер.JSON.parse(ИсходнаяСтрока);
Данные1С = КонвертироватьВСписок(Парсер, JSONОбъект);

Критически важные нюансы при работе с JSON в 1С 7.7

Разберем основные практические трудности и подводные камни, с которыми вы можете столкнуться при реализации обмена данными по протоколу JSON:

  1. Проблема кодировки UTF-8: Веб-сервисы всегда обмениваются данными в кодировке UTF-8. Платформа 1С 7.7 является исключительно ANSI-приложением (работает в кодировке Windows-1251). Если в JSON-строке присутствует кириллица, то при прямой передаче в парсер вы получите нечитаемые символы («кракозябры»). Рекомендуется предварительно перекодировать JSON-строку из UTF-8 в Windows-1251 с помощью объекта ADODB.Stream перед вызовом парсера.
  2. Совместимость со старыми ОС: Метод JScript JSON.parse требует наличия в системе Internet Explorer версии 8 или выше. Если база запускается на старой ОС (например, Windows Server 2003 без обновлений), то JSON.parse вызовет ошибку. Для обеспечения обратной совместимости в таких средах лучше использовать конструкцию (new Function('return(' + strJSON + ');'))() вместо JSON.parse.
  3. Ограничение на размер данных: При передаче огромных строк (более нескольких мегабайт) напрямую через OLE-границу между 1С и JScript может возникнуть переполнение стека вызова. Если вам требуется разбирать файлы больших объемов, загружайте их содержимое непосредственно внутри JScript с использованием Scripting.FileSystemObject или ADODB.Stream, чтобы минимизировать нагрузку на память платформы 1С.

Использование описанных выше подходов и функций-мостов позволит вам создавать надежные, современные и высокопроизводительные интеграционные механизмы в 1С 7.7, полностью закрывая вопросы разбора сложных вложенных структур и массивов JSON. При необходимости можно воспользоваться библиотекой процедур и функций для 1С 7.7 с готовыми модулями обработки JSON и HTTP.

← На главную