Почему 1С 7.7 аварийно завершает работу при парсинге больших строк JSON через MSScriptControl и как это исправить?

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

При интеграции 1С 7.7 с современными веб-сервисами и REST API разработчики часто сталкиваются с форматом JSON. В платформе 1С:Предприятие 7.7 нет встроенных средств сериализации и десериализации JSON, поэтому классическим подходом стало использование внешнего скриптового движка MSScriptControl.ScriptControl на языке JScript. Однако при получении объемного ответа от сервера система может внезапно завершить работу без генерации понятного описания ошибки. Разберем по шагам, почему возникает сбой и какими способами можно стабильно разобрать большие структуры данных.

Анализ типовой реализации и места возникновения ошибки

Посмотрим на типовой пример кода, используемый для разбора JSON в среде 1С 7.7:


ScrptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
ScrptCtrl.Language = "jscript";

КодНаJScript = 
"function parseJSON(strJSON) {
|   var tmpFunc = (new Function('return(' + strJSON + ');'))();
|   return(tmpFunc);
|}
|function aGet(Array, index) {
|   return(Array[index]);
|}
|function oValueByKey(Obj, key) {
|   return(Obj[key]);
|}
|function oSize(Obj) {
|   var size = 0, key;
|   for (key in Obj) {
|       if (Obj.hasOwnProperty(key)) size++;
|   }
|   return(size);
|}
|function eType(Element) {
|   if (Element instanceof Array) {
|       return('array');
|   } else {
|       return(typeof(Element));
|   }
|}";

ScrptCtrl.AddCode(КодНаJScript);
Объект = ScrptCtrl.Run("parseJSON", СтрокаОтвета);

При небольших объемах данных (до нескольких десятков килобайт) данный скрипт отрабатывает корректно. Но как только переменная СтрокаОтвета содержит тяжелый массив данных, строка ScrptCtrl.Run("parseJSON", СтрокаОтвета) приводит к аварийному закрытию приложения (Crash / Access Violation).

Выясним причины сбоя платформы и скриптового движка

Проанализируем технические факторы, которые вызывают аварийное завершение:

  1. Ошибка маршалинга длинных строк в платформе 1С 7.7. В ядре 1С 7.7 строки хранятся в однобайтовой кодировке ANSI. При передаче строкового параметра в COM-интерфейс IDispatch платформа выполняет преобразование в двухбайтовую строку Unicode (BSTR). При передаче строк большого объема (от нескольких сотен килобайт) во внутреннем буфере механизма OLE платформы переполняется память, что вызывает мгновенный сбой процесса 1cv7.exe.
  2. Переполнение стека компилятора JScript. Конструкция new Function('return(' + strJSON + ');') заставляет движок JScript компилировать весь объемный JSON как фрагмент исполняемого программного кода. Для больших списков или глубокой вложенности парсер скрипта исчерпывает выделенный стек синтаксического анализатора (Stack Overflow).
  3. Уязвимость к служебным символам. Если в тексте JSON встречаются неэкранированные спецсимволы, реальные разрывы строк или нулевые байты, генерация динамической функции через конкатенацию ломает синтаксис скрипта, порождая неустранимые ошибки исполнения.

Способ 1. Исключение передачи длинной строки через промежуточный файл

Чтобы избежать ошибки передачи огромной строки из 1С в COM-объект, передадим заботу о чтении файла самому скрипту. В метод передается только путь к файлу на диске, который читается напрямую средствами Windows Script Host.

Рассмотрим реализацию чтения файла через поток ADODB.Stream, что одновременно гарантирует правильную работу с кодировкой UTF-8:


КодНаJScript = 
"function parseJSONFromFile(filePath) {
|   var stream = new ActiveXObject('ADODB.Stream');
|   stream.Type = 2; // adTypeText
|   stream.Charset = 'utf-8';
|   stream.Open();
|   stream.LoadFromFile(filePath);
|   var strJSON = stream.ReadText();
|   stream.Close();
|   var tmpFunc = (new Function('return(' + strJSON + ');'))();
|   return(tmpFunc);
|}";

ScrptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
ScrptCtrl.Language = "jscript";
ScrptCtrl.AddCode(КодНаJScript);

// Сохраняем ответ во временный файл
ИмяФайла = КаталогВременныхФайлов() + "response.json";
// (запись ответа в файл средствами 1С или прямое сохранение из HTTP-запроса)

Объект = ScrptCtrl.Run("parseJSONFromFile", ИмяФайла);

Данный подход полностью решает проблему падения ядра 1С при вызове метода, так как в MSScriptControl передается лишь короткая строка пути к файлу.

Способ 2. Получение данных непосредственно внутри скрипта

Если ответ запрашивается по протоколу HTTP/HTTPS, целесообразно исключить передачу тела ответа в переменные 1С. Запрос выполняется объектом MSXML2.ServerXMLHTTP прямо внутри контекста скрипта:


КодНаJScript = 
"function loadAndParseJSON(url) {
|   var http = new ActiveXObject('MSXML2.ServerXMLHTTP');
|   http.open('GET', url, false);
|   http.send();
|   var strJSON = http.responseText;
|   var parse = (new Function('return(' + strJSON + ');'))();
|   return(parse);
|}";

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

Способ 3. Использование внешних библиотек и парсеров

Если размер ответа превышает несколько мегабайт, встроенный движок MSScriptControl все равно может аварийно завершаться из-за ограничений new Function. В таком случае рекомендуются следующие архитектурные решения:

  1. Вспомогательная библиотека OleExSup.dll. Специализированная библиотека для 1С 7.7, исправляющая внутреннюю ошибку платформы при пересылке длинных BSTR-строк в OLE-объекты.
  2. Внешняя компонента 1sqlite (модуль JSON1). Наиболее производительное решение для 1С 7.7. Позволяет загрузить текст JSON в виртуальную таблицу SQLite и разбирать элементы с помощью SQL-функций json_extract или json_tree с прямой выгрузкой результатов в объект ТаблицаЗначений.
  3. Конвертация в XML или использование Scripting.Dictionary. Для сохранения древовидной структуры можно воспользоваться COM-объектом Scripting.Dictionary или промежуточной конвертацией JSON в XML, разбор которого в 1С 7.7 хорошо отлажен через DOM-модель MSXML2.DOMDocument.
← На главную