Тема не нова, однако при реализации одного проекта столкнулся с отсутствием нормального механизма работы с данными типа JSON. И да, я не последователь "Любителей ВК", если только это не является действительно необходимым.
Так что, немало порыскав на просторах Бездны, и надергав оттуда идей, проявив недюжинную храбрость и сообразительность... )) Ну, Вы поняли
А еще я сторонник идеи "Информация для всех", потому выложу все тексты для широких масс, благо букв хоть и много, но не слишком. Может, Иегова сочтет за благое деяние (привет Леша).
Тексты в программном коде немного "уплывают" из ограничений в длине строки публикации, в нескольких местах, возможно, придется "обработать напильником" - убрать лишние переносы строк.
Источники:
https://gist.github.com/sthor69/8398788
https://forum.mista.ru/topic.php?id=799040
Ниже текст обработки "JSON_Туда_И_Обратно.ert"
//Обработка предназначена для сериализации/парсинга JSON
//На вход подается либо строка (СтрокаJSON), либо Объект (ОбъектJSON). Описание ниже. Значения типа undefined недопустимы.
//Направление: 0 - парсить, все остальные значения - сериализовать
//Из-за ограничений типизации данных в 1С при преобразовании выполняется несколько "подстав": 1. значения типа null в 1С не определяются,
//поэтому программно преобразовываются в строку "null"; 2. значения литералов false и true 1С сама преобразует в 0 и -1 соответственно;
//3. при сериализации выполняется обратное преобразование по правилам: значения строкового типа "null","false" и "true" преобразуются в литералы в контексте javascript
//
//СтрокаJSON: строка для парсинга, представляющая собой ВАЛИДНЫЙ объект JSON
//ОбъектJSON: СписокЗначений или ТаблицаЗначений для сериализации. Правила: Значение элемента списка - строка/число/СписокЗначений/ТаблицаЗначений
// Представление элемента списка: строка/число. Тип "строка" хранит значение ключа. Тип "число" - номер строки в таблице.
Процедура Скрипт_В_JSON(scriptCtrl)
лпТекст = "
|//тут следует определение класса JSON от Douglas Crockford, для браузеров где его нет
|if (typeof JSON !== 'object') {
| JSON = {};
|}
|
|(function () {
| 'use strict';
|
| function f(n) {
| // Format integers to have at least two digits.
| return n < 10 ? '0' + n : n;
| }
|
| if (typeof Date.prototype.toJSON !== 'function') {
|
| Date.prototype.toJSON = function () {
|
| return isFinite(this.valueOf())
| ? this.getUTCFullYear() + '-' +
| f(this.getUTCMonth() + 1) + '-' +
| f(this.getUTCDate()) + 'T' +
| f(this.getUTCHours()) + ':' +
| f(this.getUTCMinutes()) + ':' +
| f(this.getUTCSeconds()) + 'Z'
| : null;
| };
|
| String.prototype.toJSON =
| Number.prototype.toJSON =
| Boolean.prototype.toJSON = function () {
| return this.valueOf();
| };
| }
|
| var cx,
| escapable,
| gap,
| indent,
| meta,
| rep;
|
|
| function quote(string) {
|
| escapable.lastIndex = 0;
| return escapable.test(string) ? '""' + string.replace(escapable, function (a) {
| var c = meta[a];
| return typeof c === 'string'
| ? c
| : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
| }) + '""' : '""' + string + '""';
| }
|
|
| function str(key, holder) {
|
| var i, // The loop counter.
| k, // The member key.
| v, // The member value.
| length,
| mind = gap,
| partial,
| value = holder[key];
|
|
| if (value && typeof value === 'object' &&
| typeof value.toJSON === 'function') {
| value = value.toJSON(key);
| }
|
| if (typeof rep === 'function') {
| value = rep.call(holder, key, value);
| }
|
| switch (typeof value) {
| case 'string':
| return quote(value);
|
| case 'number':
|
| return isFinite(value) ? String(value) : 'null';
|
| case 'boolean':
| case 'null':
|
| return String(value);
|
| case 'object':
|
| if (!value) {
| return 'null';
| }
|
| gap += indent;
| partial = [];
|
| if (Object.prototype.toString.apply(value) === '[object Array]') {
|
| length = value.length;
| for (i = 0; i < length; i += 1) {
| partial[i] = str(i, value) || 'null';
| }
|
| v = partial.length === 0
| ? '[]'
| : gap
| ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
| : '[' + partial.join(',') + ']';
| gap = mind;
| return v;
| }
|
| if (rep && typeof rep === 'object') {
| length = rep.length;
| for (i = 0; i < length; i += 1) {
| if (typeof rep[i] === 'string') {
| k = rep[i];
| v = str(k, value);
| if (v) {
| partial.push(quote(k) + (gap ? ': ' : ':') + v);
| }
| }
| }
| } else {
|
| for (k in value) {
| if (Object.prototype.hasOwnProperty.call(value, k)) {
| v = str(k, value);
| if (v) {
| partial.push(quote(k) + (gap ? ': ' : ':') + v);
| }
| }
| }
| }
|
| v = partial.length === 0
| ? '{}'
| : gap
| ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
| : '{' + partial.join(',') + '}';
| gap = mind;
| return v;
| }
| }
|
| if (typeof JSON.stringify !== 'function') {
| escapable = /[\\\""\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
| meta = { // table of character substitutions
| '\b': '\\b',
| '\t': '\\t',
| '\n': '\\n',
| '\f': '\\f',
| '\r': '\\r',
| '""' : '\\""',
| '\\': '\\\\'
| };
| JSON.stringify = function (value, replacer, space) {
|
| var i;
| gap = '';
| indent = '';
|
| if (typeof space === 'number') {
| for (i = 0; i < space; i += 1) {
| indent += ' ';
| }
|
| } else if (typeof space === 'string') {
| indent = space;
| }
|
| rep = replacer;
| if (replacer && typeof replacer !== 'function' &&
| (typeof replacer !== 'object' ||
| typeof replacer.length !== 'number')) {
| throw new Error('JSON.stringify');
| }
|
| return str('', {'': value});
| };
| }
|
|
| if (typeof JSON.parse !== 'function') {
| cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
| JSON.parse = function (text, reviver) {
|
| var j;
|
| function walk(holder, key) {
|
| var k, v, value = holder[key];
| if (value && typeof value === 'object') {
| for (k in value) {
| if (Object.prototype.hasOwnProperty.call(value, k)) {
| v = walk(value, k);
| if (v !== undefined) {
| value[k] = v;
| } else {
| delete value[k];
| }
| }
| }
| }
| return reviver.call(holder, key, value);
| }
|
| text = String(text);
| cx.lastIndex = 0;
| if (cx.test(text)) {
| text = text.replace(cx, function (a) {
| return '\\u' +
| ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
| });
| }
|
| if (/^[\],:{}\s]*$/
| .test(text.replace(/\\(?:[""\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
| .replace(/""[^""\\\n\r]*""|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
| .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
| j = eval('(' + text + ')');
|
| return typeof reviver === 'function'
| ? walk({'': j}, '')
| : j;
| }
|
| throw new SyntaxError('JSON.parse');
| };
| }
|}());
|//конец определения класса JSON от Douglas Crockford
|";
scriptCtrl.AddCode(лпТекст);
КонецПроцедуры
Процедура Скрипт_Из_JSON(scriptCtrl)
лпТекст = "
| // УСТАНОВИТЬ свойство объекта или массива
| function aPost(Array, index, value) {
| if (value == ""null"") {value = null}
| if (value == ""false"") {value = false}
| if (value == ""true"") {value = true}
| if (typeof value === 'number') {value = +value.toFixed(15)}
| Array[index] = value;
| }
|
| // Получить элемент массива по индексу
| function aGet(Array, index) {
| return(Array[index]);
| }
|
| // Получить ключ пары по индексу
| function oKey(Obj, index) {
| var size = 0, key;
| for (key in Obj) {
| if (size == index) break;
| if (Obj.hasOwnProperty(key)) size++;
| }
| return(key);
| }
|
| // Получить значение пары по ключу
| function oValueByKey(Obj, key) {
| return(Obj[key]);
| }
|
| //Получить количество пар в объекте
| Object.size = function(obj) {
| var size = 0, key;
| for (key in obj) {
| if (obj.hasOwnProperty(key)) size++;
| }
| return(size);
| }
|
| //Получить размер объекта (количество пар в нём)
| function oSize(Obj) {
| return(Object.size(Obj));
| }
|
| // Получить тип объекта (number, string, object, array)
| function eType(Element) {
| if (Element instanceof Array) {
| return(""array"");
| } else if (Object.prototype.toString.call(Element) === '[object Array]') {
| return(""array"");
| } else if (Element === null || Element == null) {
| return(""null"");
| } else {
| return(typeof(Element));
| }
| }
|";
scriptCtrl.AddCode(лпТекст);
КонецПроцедуры
Функция jsonВОбъект(scriptCtrl, obj)
ТипОбъекта = scriptCtrl.run("eType", obj);
Если ТипОбъекта = "object" Тогда
сп = СоздатьОбъект("СписокЗначений");
Для н=0 По scriptCtrl.run("oSize", obj)-1 Цикл
Ключ = scriptCtrl.run("oKey", obj, н);
Значение = scriptCtrl.run("oValueByKey", obj, Ключ);
ТипЗнач = scriptCtrl.run("eType", Значение);
Если Найти("object, array", ТипЗнач) > 0 Тогда
сп.ДобавитьЗначение(jsonВОбъект(scriptCtrl,Значение), Ключ);
Иначе
Если ТипЗначенияСтр(Значение) = "" Тогда
Значение = "null";
КонецЕсли;
сп.ДобавитьЗначение(Значение, Ключ);
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипОбъекта = "array" Тогда
сп = СоздатьОбъект("ТаблицаЗначений");
сп.НоваяКолонка("Значение");
Для н=0 По scriptCtrl.run("oSize", obj)-1 Цикл
сп.НоваяСтрока();
Значение = scriptCtrl.run("aGet", obj, н);
ТипЗнач = scriptCtrl.run("eType", Значение);
Если Найти("object, array", ТипЗнач) > 0 Тогда
сп.Значение = jsonВОбъект(scriptCtrl,Значение);
Иначе
Если ТипЗначенияСтр(Значение) = "" Тогда
Значение = "null";
КонецЕсли;
сп.Значение = Значение;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат сп;
КонецФункции
Функция ОбъектВjson(scriptCtrl, Об, СпсТЗ)
лпСпс = СоздатьОбъект("СписокЗначений");
Если ТипЗначенияСтр(СпсТЗ) = "СписокЗначений" Тогда
СпсТЗ.Выгрузить(лпСпс);
ИначеЕсли ТипЗначенияСтр(СпсТЗ) = "ТаблицаЗначений" Тогда
СпсТЗ.ВыбратьСтроки();
Пока СпсТЗ.ПолучитьСтроку()=1 Цикл
лпСпс.ДобавитьЗначение(СпсТЗ.Значение,СпсТЗ.НомерСтроки-1);
КонецЦикла;
КонецЕсли;
Для i=1 По лпСпс.РазмерСписка() Цикл
Ключ = "";
Значение = лпСпс.ПолучитьЗначение(i,Ключ);
Если (ТипЗначенияСтр(Значение)="СписокЗначений") ИЛИ (ТипЗначенияСтр(Значение)="ТаблицаЗначений") Тогда
Если ТипЗначенияСтр(Значение)="ТаблицаЗначений" Тогда //массив
ОбВл = scriptCtrl.Eval("new Array()");
Иначе
ОбВл = scriptCtrl.Eval("new Object()");
КонецЕсли;
Значение = ОбъектВjson(scriptCtrl, ОбВл, Значение);
КонецЕсли;
scriptCtrl.run("aPost", Об, Ключ, Значение);
КонецЦикла;
возврат Об;
КонецФункции
//*********************************************************************************
Функция СкриптФормироватьСтроку(прСтрПарам, прПерем, scriptCtrl) Экспорт
Если (ПустоеЗначение(scriptCtrl) = 1) Тогда
scriptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
scriptCtrl.Language="javascript";
КонецЕсли;
scriptCtrl.Eval("var "+прПерем+"=''");
лпСтрПарам = прСтрПарам;
Пока (СтрДлина(лпСтрПарам)>0) Цикл
Если СтрДлина(лпСтрПарам) <= 200000 Тогда
лпЛевСтрПарам = лпСтрПарам;
лпСтрПарам = "";
Иначе
лпЛевСтрПарам = Лев(лпСтрПарам, 200000);
лпСтрПарам = Сред(лпСтрПарам, 200001);
КонецЕсли;
лпЛевСтрПарам = СтрЗаменить(лпЛевСтрПарам,"\","\\"); //для JS
лпЛевСтрПарам = СтрЗаменить(лпЛевСтрПарам,"'","\'"); //для JS
scriptCtrl.Eval(прПерем+"="+прПерем+" + '"+лпЛевСтрПарам+"'");
КонецЦикла;
Возврат scriptCtrl;
КонецФункции
Процедура ПриОткрытии()
статусвозврата(0);
Конт = Форма.Параметр;
Данные = Конт.Получить("Вход");
Чсл = Конт.Получить("Направление");
scriptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl");
scriptCtrl.Language="javascript";
scriptCtrl.reset();
Скрипт_Из_JSON(scriptCtrl);
Скрипт_В_JSON(scriptCtrl);
Попытка
Если Чсл = 0 Тогда //из JSON
Если ТипЗначенияСтр(Данные) <> "Строка" Тогда
Конт.Установить("Выход", "На входе должна быть строка");
возврат;
КонецЕсли;
СкриптФормироватьСтроку(Данные, "strparam", scriptCtrl);
Объект = scriptCtrl.eval("JSON.parse(strparam, '')"); //второй параметр - функция преобразования значений. например: function(k, v) {console.log(k); return v;}. k - свойство, v - значение
//значение true возвращается как -1, значение fales возвращается как 0
Спс = jsonВОбъект(scriptCtrl, Объект);
Конт.Установить("Выход", Спс);
Иначе //в JSON
Если (ТипЗначенияСтр(Данные) <> "СписокЗначений") И (ТипЗначенияСтр(Данные) <> "ТаблицаЗначений") Тогда
Конт.Установить("Выход", "На входе должен быть или Список или Таблица");
возврат;
КонецЕсли;
Класс = scriptCtrl.eval("JSON");
Если ТипЗначенияСтр(Данные) = "СписокЗначений" Тогда
Об = scriptCtrl.Eval("new Object()");
Иначе
Об = scriptCtrl.Eval("new Array()");
КонецЕсли;
ОбъектВjson(scriptCtrl, Об, Данные);
Стр = Класс.stringify(Об); //второй параметр: 1 - функция. пример: function(key, value) {if (typeof value === 'string') {return undefined; } return value; }.
//2 - массив с именами возвращаемых свойст. пример: ['week', 'month']. Третий параметр: 1 - число, определяющее количество пробелов в начале каждого блока
//умноженное на уровень вложенности блока. 3 - строка. каждый следующий уровень блока будет начинатся с нее. Максимальное значение: 10 символов
Конт.Установить("Выход", Стр);
КонецЕсли;
Исключение
Конт.Установить("Выход", ОписаниеОшибки());
КонецПопытки;
scriptCtrl.reset();
scriptCtrl = 0;
КонецПроцедуры
А тут небольшой пример ее использования
Функция ОбработатьДанныеJSON(прДанные,прНаправление)
лпКаталог = "";
РасположениеФайла(лпКаталог);
лпСписокЗадач = СоздатьОбъект("СписокЗначений");
лпСписокЗадач.Установить("Вход",прДанные);
лпСписокЗадач.Установить("Направление",прНаправление);
ОткрытьФорму("Отчет", лпСписокЗадач, лпКаталог+"JSON_Туда_И_Обратно.ert");
возврат лпСписокЗадач.Получить("Выход");
КонецФункции
Функция ПолучитьСписокИзСтроки(прСтрока, прРазделитель) Экспорт
лпСписокВозв = СоздатьОбъект("СписокЗначений");
лпСтрока = СтрЗаменить(прСтрока, прРазделитель, РазделительСтрок);
лпФайлСтрока = СоздатьОбъект("Текст");
лпФайлСтрока.ДобавитьСтроку(лпСтрока);
лпКолвоСтрок = лпФайлСтрока.КоличествоСтрок();
Для лпИндС = 1 По лпКолвоСтрок Цикл
лпСтрЗнач = СокрЛП(лпФайлСтрока.ПолучитьСтроку(лпИндС));
лпСписокВозв.ДобавитьЗначение(лпСтрЗнач);
КонецЦикла;
Возврат лпСписокВозв;
КонецФункции
Функция ПолучитьЗначениеИзСписка(прСпс, прУровень)
лпЗначение = прСпс;
лпСпсУровней = ПолучитьСписокИзСтроки(прУровень, "/");
Для i=1 По лпСпсУровней.РазмерСписка() Цикл
лпЗначСпсУровней = лпСпсУровней.ПолучитьЗначение(i);
Если ТипЗначенияСтр(лпЗначение) = "СписокЗначений" Тогда //это представление
лпЗначение = лпЗначение.Получить(лпЗначСпсУровней);
ИначеЕсли ТипЗначенияСтр(лпЗначение) = "ТаблицаЗначений" Тогда //это номер
Если лпЗначение.КоличествоСтрок() < Число(лпЗначСпсУровней)+1 Тогда
лпЗначение = ПолучитьПустоеЗначение();
Иначе
лпЗначение = лпЗначение.ПолучитьЗначение(Число(лпЗначСпсУровней)+1,"Значение");
КонецЕсли;
Иначе
лпЗначение = ПолучитьПустоеЗначение();
КонецЕсли;
Если ПустоеЗначение(лпЗначение) = 1 Тогда
прервать;
КонецЕсли;
КонецЦикла;
возврат лпЗначение;
КонецФункции
Процедура Сформировать()
лпТекст = "{""title"":""Conference"",""room"":{""number"":23,""participants"":[""john"",""ann""]}}";
лпСпс = ОбработатьДанныеJSON(лпТекст,0);
лпid = ПолучитьЗначениеИзСписка(лпСпс, "room/participants/1");
сообщить(лпid);
лпТекст = ОбработатьДанныеJSON(лпСпс,1);
сообщить(лпТекст);
КонецПроцедуры
Обновлено 07.06.21:
Исправлена ошибка при работе с числами с плавающей точкой
Использован механизм формирования длинной строки "внутри" скрипта функцией СкриптФормироватьСтроку (обход критической ошибки вызывающей крах приложения при передаче длинной строки в скрипт)