|
v7: Парсинг JSON с помошью SQLLite и значения булево | ☑ | ||
---|---|---|---|---|
0
MWWRuza
10.02.24
✎
01:35
|
Добрый день.
Это в продолжение моей темы: Можно ли получить инфу о контрагенте по его GUID в ЦРПТ - ? но, не по тому вопросу, из-за которого та тема создана. Просто вылезло на том-же отчете. Писал и отлаживал на не большой базе, все работало. Поставил реальному клиенту - 1С стала падать на нем... Стал разбираться почему... Оказалость, парсер JSON, которым я давно и успешно пользовался, не переваривает большие файлы, а они там действительно большие... Ладно, хороший повод воспользоваться SQLLite. Переделал отчет на ее использование, и все заработало, ничего не падает. И все бы хорошо, но в старом парсере булево было представлено как "-1" - true, и "0" - false. SQLLite возвращает как "1" и "0"... Как отличить "1" что это "Да", от того, что "1" это число? Состав таблицы отчета не постоянный, показатели и их значения "плавающие", зависят от многих факторов, так, что "по смыслу" не понять что это - число или булево... Ну, с нулем то еще можно мириться, там особо нет значений нулевых, а вот единица встречается часто... И не понять, где "Да", а где число 1... Раньше было "-1", которое не всречается в этом отчете, выводил тупо "Да" на "-1", и как-бы устраивало. ![]() ![]() Что-бы такого придумать, что-бы "обойти" эту ситуацию-? Пока видится только вариант создания списка реквизитов, которые могут иметь значение только булево, и проверять их по этому списку при выводе отчета, типо если есть имя этого поля(Ключ) в этом списке, то интерпретировать 1/0 как Да/Нет, а если нет в списке, то выводить без преобразования, числами... Но, как-то это криво... Наши закоготворцы не дремлют, и их прогеры тоже стараются во всю, в любой момент могут добавить какие-то дополнительные показатели, которых нет в списке, и опять добавляй... Может есть какой-то более правильный "костыль" - ? Или может каким-то параметром запроса SQLLite можно отключить это преобразование, что-бы просто строками выводил "true/false" - ? |
|||
2
Волшебник
10.02.24
✎
10:05
|
(0) Надо анализировать имя поля. Если там есть "Flag" или "is", то скорее всего это булево.
С помощью статистики и человека составить список таких реквизитов. |
|||
3
vbus
10.02.24
✎
10:10
|
Пусть парсер пишет Истина или Ложь в поле TEXT, этого реквизита.
|
|||
4
Волшебник
10.02.24
✎
10:15
|
Ещё можно анализировать значение в других элементах. Если там встречается 2 или другие цифры, то это наверняка число, а если только 1 и 0, то есть повышенная вероятность булева типа.
|
|||
5
MWWRuza
10.02.24
✎
11:29
|
(2) С помощью статистики и человека составить список таких реквизитов.
Ну, тут есть вариант проще - в любой более-менее крупной конторе(да хотя-бы в той, на которой предыдущий парсер падал - там порядка ~2500 карточек), сохранить этот JSON ответа в файл, открыть его тем-же Нотепад++ с плагином, отформатировать, что-бы он разбился на кучу строк, а потом написать обработку, которая пробежит по строкам этого файла как по обычному тексту, без парсинга, и просто соберет все имена ревизитов, из строк, в которых есть "true" или "false"... Ну, а потом, просто при выводе отчета анализировать есть текущий реквизит в этом списке или нет, и соответственно его обрабатывать. Не вижу проблем, час работы... Но, это: ![]() Я просто боюсь, что порачу я этот час, реализую задумку, а потом придет Djelf, "покрутит пальцем у виска", и скажет - "ребята, вы чего? Все это в топку.. Достаточно в SQLLite применить параметр <ХЗ>, и оно само будет булево передавать без перекодировки в число" :-) |
|||
6
Garykom
10.02.24
✎
11:41
|
(0) микросервис на Go
|
|||
7
Крэкпэк
10.02.24
✎
12:31
|
(0) Разбейте строку
Процедура СкриптФормироватьСтроку(прСтрПарам, прПерем, scriptCtrl) Если (ПустоеЗначение(scriptCtrl) = 1) Тогда scriptCtrl = СоздатьОбъект("MSScriptControl.ScriptControl"); scriptCtrl.Language="javascript"; КонецЕсли; scriptCtrl.Reset(); scriptCtrl.Eval("var "+прПерем+"=''"); лпСтрПарам = прСтрПарам; Пока (СтрДлина(лпСтрПарам)>0) Цикл Если СтрДлина(лпСтрПарам) <= 200000 Тогда лпЛевСтрПарам = лпСтрПарам; лпСтрПарам = ""; Иначе лпЛевСтрПарам = Лев(лпСтрПарам, 200000); лпСтрПарам = Сред(лпСтрПарам, 200001); КонецЕсли; лпЛевСтрПарам = СтрЗаменить(лпЛевСтрПарам,"\","\\"); //для JS scriptCtrl.Eval(прПерем+"="+прПерем+" + '"+лпЛевСтрПарам+"'"); КонецЦикла; КонецПроцедуры Процедура СкриптКонтролИнит(scriptCtrl) код = " | | function parseJSON_(strJSON) { | var objJSON = eval('(function(){return ' + strJSON + ';})()'); | return(objJSON); | } | | function parseJSON() { | var objJSON = eval('(' + strJSON + ')'); | return(objJSON); | } | | // Получить элемент массива | 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) { | if (Obj[key] === true||Obj[key] === false) return Obj[key].toString(); | 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 { | return(typeof(Element)); | } | } |"; scriptCtrl.AddCode(код); КонецПроцедуры и далее вызов Стр=СтрЗаменить(стр,СимволТабуляции,""); Стр=СтрЗаменить(Стр,Симв(10),""); Стр=СтрЗаменить(Стр,Симв(13),""); Стр=СтрЗаменить(Стр,"\r",""); Стр=СтрЗаменить(Стр,"\n",""); scriptCtrl=""; СкриптФормироватьСтроку(Стр,"strJSON",scriptCtrl); СкриптКонтролИнит(scriptCtrl); obj = scriptCtrl.run("parseJSON"); P.S. сори за неформатированный текст |
|||
8
MWWRuza
10.02.24
✎
17:01
|
Все оказалось проще :-)
(5) Я просто боюсь, что потрачу я этот час, реализую задумку, а потом придет Djelf, "покрутит пальцем у виска", и скажет - "ребята, вы чего? Все это в топку.. Так и есть, в более свежих сборках SQLLite, в ТЗ ответа запроса, есть колонка "type": ![]() А дальше "уже дело техники" - на основании этих двух колонок уже в 1С можно формировать Да/Нет или число :-) Супер! Все просто и удобно. Ровно шесть коротких строчек кода, и все идеально: ![]() И даже, в отличии от предыдущего парсера, если в значениях встретится "0" или "-1", то все равно сформируется в печатной форме правильно, где надо число, а где надо - булево. |
|||
9
Волшебник
10.02.24
✎
15:38
|
(8) 👍
|
|||
11
Aleksey
10.02.24
✎
16:24
|
(8) А где почитать про "Парсинг JSON с помошью SQLLite"
|
|||
12
MWWRuza
10.02.24
✎
16:58
|
(11) Там все очень просто - у Djelf в демке обработки по карлику было...
А вообще, вот у меня так сделано: Функция РазборJSON_SQLLite(ТекстJSON) Попытка База = СоздатьОбъект("SQLiteBase"); Исключение ЗагрузитьВнешнююКомпоненту("1sqlite.dll"); База = СоздатьОбъект("SQLiteBase"); КонецПопытки; База.Открыть(":memory:"); Запрос = База.НовыйЗапрос(); Запрос.Подготовить(" |SELECT | * |FROM json_tree(@ТекстJSON) |"); Запрос.УстановитьПараметр("@ТекстJSON",ТекстJSON); тзДерево = Запрос.Выполнить(); Возврат тзДерево; КонецФункции Естественно, 1sqlite.dll должна быть достаточно свежая, старые этого не понимают, ошибку пишут. |
|||
13
Крэкпэк
10.02.24
✎
17:18
|
(12) и как данный код справляется при передачи большой строки ?
|
|||
14
MWWRuza
10.02.24
✎
17:30
|
(13) Хм... Вопрос конечно интересный, пока тестировал строку, которая в файле при сохранениии весит 1 мегабайт... Если с большей не будет справляться, из-за ограничения длины строки,то буду думать как переделать на файл или поток.
Но, одно могу сказать - парсер отсюда: https://github.com/r72cccp/1C77_JSON_parser/ падает и на половине размера этих данных, из-за чего и стал переделывать на SQLLite... |
|||
15
Крэкпэк
10.02.24
✎
17:41
|
(14) попробуй мой код (7) там как раз для длинных строк
|
|||
16
MWWRuza
10.02.24
✎
18:17
|
Вообще, конечно, косяк архитектуры API ЧЗ... Я запрашиваю остатки/движения, возвращаются движения по GTIN, без наименований... Ну, хорошо, по GTIN можно получить карточки товаров, и из них взять "человеко-читаемые" наименования товаров. Но, это огромный файл, так, как карточки товаров содержат кроме GTIN и наименования, еще кучу данных. Да, это упрощает расшифровку значений ячеек таблицы - не надо ничего запрашивать, данные и так уже получены.
Но, насколько легче все это было-бы, если бы ответ содержал не только GTIN, но и Наименования, а делать запрос конкретной карточки с полными данными по ее GTIN только при необходимости, при расшифровке(обработке ячейки таблицы)? Да, я понимаю, когда мы смотрим фильмы по сети, трафик в сотни, а то и в тысячи раз больше... Но, все равно, зачем гонять по сети лишнюю, никому не нужную инфу? |
|||
17
MWWRuza
10.02.24
✎
17:49
|
(15) Попробую. Потом отпишусь.
|
|||
18
Djelf
10.02.24
✎
20:44
|
(12) Разбор JSON довольно старая штука в sqlite, но пока не было ни моих потребностей, ни потребностей пользователей и я это не задействовал.
И json не входил с сборку (не помню когда я стал его по-умолчанию включать), из-за этого и была ошибка. Сейчас это стало уже везде востребовано и всегда уже будет во всех сборках (ничтожный оверхед по объему). Из интересного и свежего: в sqlite3 сейчас появился разбор в jsonb(ТекстJson) это блоб и может быть сохранен в БП sqlite3, размер меньше, скорость выборки в 10 раз (обещают) быстрее. Будем посмотреть... Волшебник, спасибо за возращение прав на редактирование! Я всякую ерунду писал, постараюсь больше такого не делать. |
|||
19
Aleksey
10.02.24
✎
20:57
|
(15) Докладаю. У меня есть загрузка заказов с сайта. и когда большой заказ (примерно 150-200 строк и больше), то 1С ка падала на таких заказах (сайт ответ дает в JSONе).
С твоим кодом заказ на 257 позиций загрузился и не споткнулся. По крайне мере визуально по сумме заказ и количеству позиций сошлось. |
|||
20
MWWRuza
11.02.24
✎
00:36
|
(19) (15) Докладаю.
Ну, тогда и я немного добавлю. Протестировал сегодня у клиента с довольно солидным документооборотом. Примерно 2400 карточек маркированных товаров ЧЗ отдает. По любому, больше 1000 в запрос не засунуть - у них ограничение. Скриншот из PDF-ки описания API: ![]() Поэтому, приходится делать несколько запросов, делить всю эту кучу на несколько по 1000 + остаток, через в цикле СчGTIN%1000 = 0. Получается два запроса по 1000, и один 400 кодов. Сохранил ради интереса строки JSON в файлы, и что получилось: ![]() Один из "больших" файлов в Нотепад++ : ![]() Почти три миллиона символов. "Склеиваю" полученные таблицы в одну, я уже потом, после парсинга, перед выводом печатной формы отчета. Глотает и не давится, если не считать те секунды, в течении которых сервер отрабатывает запросы, то парсинг с помощью SQLLite отрабатывает практически мгновенно :-) Djelf, еще раз спасибо за очень полезную вещь :-) |
|||
21
MWWRuza
11.02.24
✎
00:47
|
Да, кстати, по теме сабжа. Переделал по совету Djelf запрос так:
Функция РазборJSON_SQLLite(ТекстJSON) Попытка База = СоздатьОбъект("SQLiteBase"); Исключение ЗагрузитьВнешнююКомпоненту("1sqlite.dll"); База = СоздатьОбъект("SQLiteBase"); КонецПопытки; База.Открыть(":memory:"); Запрос= База.НовыйЗапрос(); Запрос.Подготовить(" |SELECT | CASE | WHEN tree.type == 'true' THEN 'Да' | WHEN tree.type == 'false' THEN 'Нет' | ELSE tree.value | END AS Значение, | tree.key AS Ключ, | tree.path AS Путь, | tree.type AS Тип |FROM json_tree(@ТекстJSON) AS tree |WHERE tree.key > '' |"); Запрос.УстановитьПараметр("@ТекстJSON",ТекстJSON); тзДерево = Запрос.Выполнить(); Возврат тзДерево; КонецФункции Теперь все булево/числа внутри запроса преобразуются, и прилетают в 1С в готовом виде - "Да" или "Нет" или число :-) |
|||
22
Крэкпэк
11.02.24
✎
15:01
|
(21) ну если есть 1с++, база SQL и уровень совместимости БД MS SQL Server 2016 и выше то будет работать такое
Запрос=СоздатьОбъект("ODBCRecordset"); ТекстЗапроса=" |SELECT * |FROM OPENJSON(:Стр, '$.""Содержимое""') |WITH |( | [Идентификатор] varchar(36) |, [НомерДокумента] varchar(20) |) |"; Запрос.УстановитьТекстовыйПараметр("Стр",СокрЛП(Стр)); Рез=Запрос.ВыполнитьИнструкцию(ТекстЗапроса); где Стр = " { "Содержимое": [ { "Идентификатор": "8D943717-37C5-4D43-B5EF-341B3F065C0F", "НомерДокумента": "Ю000000611" } ] } и никаких дополнительных прокладок не нужно |
|||
23
Djelf
11.02.24
✎
16:42
|
(22) А "БД MS SQL Server 2016 и выше это не прокладка?" Тоже прокладка, просто это другая прокладка.
Но так тоже можно, и еще двумя десятками различных вариантов прокладок... |
|||
24
MWWRuza
11.02.24
✎
16:57
|
(22) база SQL и уровень совместимости БД MS SQL Server 2016 и выше
Там много чего можно... Но, в том-то и дело что магазинчики маленькие, и ни о каких "базах SQL" речи не идет. База обычная DBF. |
|||
25
Крэкпэк
11.02.24
✎
17:14
|
(23) имелось ввиду что база уже работает под управлением SQL
а так конечно согласен по поводу кучи вариантов (24) с DBF конечно не вариант с другой стороны может кому то пригодится |
|||
26
Djelf
12.02.24
✎
05:33
|
Так с чем сравнивать то? Меньше полутора метров всего занимает 1sqlite, без установки в системе и регистрации в реестре.
Ну давайте, поставьте MS SQL на тех же условиях, или еще что-то... Конкурентов тут нет. |
|||
27
serpentt
12.02.24
✎
11:45
|
Здравствуйте, какая сейчас актуальная версия 1sqlite и где ?
|
|||
28
Djelf
12.02.24
✎
11:57
|
(27) Последняя... Тут все: https://cloud.mail.ru/public/9znr/ZJ6ULE9aR
|
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |