Разбор JSON

rinsk

Lotus Team
12.11.2009
1 156
126
BIT
43
LSJsonParser. Пользоваться тоже можно)
Добавлено: Хотел ещё JSONObject переименовать в JSONList, а в JSONObject перенести общие методы и сделать его базовым классом, но пока оставил так, для обратной совместимости.
Добавил (если не против конечно:) методы toJsonString к обоим классам и в JSONObject скорректировал функцию AddItem:
Visual Basic:
// в класс JSONObject
    Public Function toJsonString() As String
        GoTo begin
errors:Error Err, Error & " |" & GetThreadInfo(1) & " in Line:" & Erl & "|"
begin:On Error GoTo errors
        Dim k As Integer
        Dim ret() As String
        Dim tname$
        If me.m_size=0 Then
            toJsonString=|{}|
        Else
            ReDim ret(me.m_size-1) As String
            ForAll m In m_items
                tname=TypeName(m)
                Select Case tName
                Case "JSONARRAY", "JSONOBJECT"
                    ret(k)=|"|+ListTag(m)+|":|+m.toJsonString()
                Case "STRING"
                    ret(k)=|"|+ListTag(m)+|":"|+JSON_EscapeString(m)+|"|
                Case "DOUBLE","INTEGER"       
                    ret(k)=|"|+ListTag(m)+|":|+CStr(m)
                Case "NOTESDATETIME","DATE"       
                    ret(k)=|"|+ListTag(m)+|":|+Format(m_items(k),DDS_JSON_DT_FORMAT) 
                Case "BOOLEAN"       
                    If m=True Then ret(k)=|"|+ListTag(m)+|":true|   Else ret(k)=|"|+ListTag(m)+|":false|                 
                Case Else
                    Error 1001," Error type of object:" & tname
            End Select
            k=k+1
            End ForAll
            toJsonString="{"+Join(ret,",")+"}"
        End If
    End Function
  
    // в класс JSONArray
    Public Function toJsonString() As String
        GoTo begin
errors:Error Err, Error & " |" & GetThreadInfo(1) & " in Line:" & Erl & "|"
begin:On Error GoTo errors
        Dim k As Integer
        Dim ret() As String
        Dim tname$
        If me.m_size=0 Then
            toJsonString=|[]|
        Else
            ReDim ret(me.m_size-1) As string
            For k=0 To me.m_size-1
                tname=TypeName(m_items(k))
                Select Case tName
                    Case "JSONARRAY","JSONOBJECT"
                           ret(k)=m_items(k).toJsonString()
                    Case "STRING"
                        ret(k)=|"|+JSON_EscapeString(m_items(k))+|"|
                    Case "DOUBLE","INTEGER"       
                        ret(k)=CStr(m_items(k))
                Case "NOTESDATETIME","DATE"       
                        ret(k)=Format(m_items(k),DDS_JSON_DT_FORMAT)                     
                    Case "BOOLEAN"       
                        If m_items(k)=True Then ret(k)="true" Else m_items(k)="false"   
                    Case Else
                                Error 1001," Error type of object"
                End Select
            Next
            toJsonString="["+Join(ret,",")+"]"
        End If
    End Function

  %REM
    '' Изменен оригинальняй метод. Если itemName уже есть, то счетчик не увеличивать
%END REM
    Public Sub AddItem(itemName As String, itemVal As Variant)
        If Not me.HasItem(itemName) Then m_size = m_size + 1
        If IsObject(itemVal) Then
            Set Me.m_items(itemName) = itemVal
        Else
            Me.m_items(itemName) = itemVal
        End If
    End Sub
%REM
    Function JSON_EscapeString
    Description: Общая ф-ция для экранирования в JSON
%END REM
Private Function JSON_EscapeString(jsonstr)
    GoTo begin
errors:Error Err, Error & " |" & GetThreadInfo(1) & " in Line:" & Erl & "|"
begin:On Error GoTo errors
    jsonstr=Replace(jsonstr,"\","\\")
    jsonstr=Replace(jsonstr,{"},{\"})
    jsonstr=Replace(jsonstr,Chr(1),{\u0001})
    jsonstr=Replace(jsonstr,Chr(2),{\u0002})
    jsonstr=Replace(jsonstr,Chr(8),{\b})
    jsonstr=Replace(jsonstr,Chr(9),{\t})
    jsonstr=Replace(jsonstr,Chr(13),{\r})
    jsonstr=Replace(jsonstr,Chr(10),{\n})
    jsonstr=Replace(jsonstr,Chr(12),{\f})
    jsonstr=Replace(jsonstr,Chr(19),{})   
    JSON_EscapeString=Replace(jsonstr,Chr(26),{\u001a})   
End Function
 
  • Нравится
Реакции: VladSh, lmike и alexas1

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
Добавил (если не против конечно:) методы toJsonString к обоим классам и в JSONObject скорректировал функцию AddItem
Конечно не против, только рад) Спасибо!

Просто у меня от предыдущего поколения для этого была совершенно ужасная функция, а класс изначально был для разбора json, потому я и не впиливал туда её:
Visual Basic:
%REM
    Function JSON_convertListToJSONString
    Создание JSON-строки по заданному массиву значений. Тип данных извлекается из самих данных, нет необходимости явно его указывать.
    pvData:
        listtag - имя поля
        value - Variant, содержащий скалярное значение нужного типа или Array of Variant c нужным типом данных
%END REM
Function JSON_convertListToJSONString(pvData List As Variant) As String
    On Error GoTo ErrH
    Dim vRes As Variant
    Dim sData As String
    Dim vData As Variant
    Dim vDataAsText() As String
    Dim nType As Integer
    Dim nI As Integer
   
    vRes = Split("")
   
    ForAll entry In pvData
        If IsArray(entry) Then
            nType = DataType(entry(0))
            vData = entry
        Else
            Dim arrTmp(0) As Variant
            arrTmp(0) = entry
           
            nType = DataType(entry)
            vData = arrTmp
        End If
       
        ReDim vDataAsText(UBound(vData))
       
        'Экранируем некоторые символы
        For nI = LBound(vData) To UBound(vData)
            If nType >= 2 And nType <= 5 Then                                        'NUMBERS
                'Дробные числа конвертируем с разделителем "."
                vDataAsText(nI) = Replace(CStr(vData(nI)), ",", ".")
            Else                                                                                'TEXT
                vDataAsText(nI) = vData(nI)
                Call JSON_EscapeString(vDataAsText(nI))
                vDataAsText(nI) = {"} + vDataAsText(nI) + {"}
            End If
        Next
       
        If UBound(vDataAsText) > 0 Then
            sData = {[} + Join(vDataAsText, {,}) +{]}
        Else
            sData = vDataAsText(0)
        End If
       
        If nType = 7 Then                                                                    'DateTime
            vRes = ArrayAppend(vRes, {"} + ListTag(entry) + {":} + "{""data"":" + sData + ",""type"":""datetime""}")
        Else
            vRes = ArrayAppend(vRes, {"} + ListTag(entry) + {":} + sData)
        End If      
    End ForAll
   
    vRes = FullTrim(vRes)
    JSON_convertListToJSONString = "{" + Join(vRes, ",") + "}"
    Exit Function
   
ErrH:
    Error Err, GetThreadInfo(1) & " (" & Erl & ") -> " & Error$
End Function

%REM
    Sub JSON_EscapeString
    Description: sJSON - строка с JSON, которая будет обработана
    Использовать только для значений полей в json!!!
%END REM
Private Sub JSON_EscapeString(sJSON As String)
    sJSON = Replace(sJSON, {\}, {\\})
    sJSON = Replace(sJSON, Chr(13) + Chr(10), "\n")
    sJSON = Replace(sJSON, Chr(13), "\n")
    sJSON = Replace(sJSON, Chr(10), "\n")
    sJSON = Replace(sJSON, Chr(9), "\t")
    sJSON = Replace(sJSON, {"}, {\"})
    sJSON = Replace(sJSON, {/}, {\/})
End Sub
Но можно и методами. Просто для разбора JSON объекты вынужденная мера, а для создания не хотелось с ними заморачиваться. Я минималист)

В любом случае что-нибудь общее и толковое вылепить из этого кода можно!
Голосуем!))) Если все ЗА, то впиливаю методы в классы)
/так плавно и на гитхаб переедем))/

Добавлено: сравнений с Java не делал. Но по коду видно, что написано хорошо, и вряд ли в самом парсинге можно что-то ускорить.
Чуть-чуть можно ускорить если заменить пользование внутренними методами на переменные-члены класса напрямую, там есть такие места.
 
Последнее редактирование:
  • Нравится
Реакции: alexas1

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
Но только что заметил большой плюс класса - при передаче параметров методом не нужно следить за тем, чтобы обрабатывать с помощью JSON_EscapeString не весь json, только значение параметров. А в функции надо следить (в коммент даже это вынес).
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
Ещё раз спасибо за AddItem! Заодно в одноимённом методе JSONArray избавился от ненужных вычислений.

JSONParser.recoveryString: добавлено обратное преобразование предложенных в JSON_EscapeString спецсимволов.
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
сравнений с Java не делал
в базовом случае смысл сравнивать отсутствует, меня вот больше интересует json<->"произвольный" Object ;)
как это в jackson и т.п.
получение ноды по пути вида нода1/нода2/...
а так - все замечательно
 
Последнее редактирование:

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
в базовом случае смысл сравнивать отсутствует, меня вот больше интересует json<->"произвольный" Object ;)
Имеешь ввиду получить общий объект, вместо специфических JSONObject и JSONArray, и работать с ним? Если да, то писал выше, что можно попробовать выделить базовый класс. Общими будут свойства Items и Size, этого должно хватить.
Не сделал пока, т.к. у нас определённые периоды между релизами с рекомпиляцией кода, потому собираю все изменения, которые требуют рекомпиляции, и вношу их одним махом. Да и хотелось оставить какую-то обратную совместимость, что ли... но видимо нужно двигаться дальше)

получение ноды по пути вида нода1/нода2/...
Добавлял метод GetItemByPath, он получает ноду черед точку: нода1.нода2. Я только через него и работаю, даже для получения ноды первого уровня, так безопаснее и удобнее.
 
Последнее редактирование:
  • Нравится
Реакции: lmike

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
вот еще это смутило, там \r символ "в классике"
У меня была идея унифицировать переводы строк для работы с DXL, потому всё гнал в "\n". Знаю, что правильно как rinsk предложил. Я обычно легко иду на контакт, и сразу же впилил в код его вариант, т.к. это обосновано. А свою специфику действительно правильно вынести в специфику)

да и вовсе - с переводами строк возможно подумать над конвертацией виддовс<->*никс
Предлагай. Кодом :) Я только ЗА.
 

rinsk

Lotus Team
12.11.2009
1 156
126
BIT
43
JSONParser.recoveryString: добавлено обратное преобразование предложенных в JSON_EscapeString спецсимволов.

Это конечно же не полный список спецсимволов - это то что я реально выловил из nsf приклада при конвертации в jsonb постгреса. Полный - достаточно тормозной, ибо там идет перебор символов:)
Сейчас как раз проект - где в POST rest параметры летят в json и что бы это json формировать ранее использовал строки. через классы - как то спортивнее:) По этому в toJsonString можно добавить обработку
и других типов LS - richText например. А еще лучше - как то перезагрузить метод для специфических применений...
 

rinsk

Lotus Team
12.11.2009
1 156
126
BIT
43
вот еще это смутило, там \r символ "в классике" да и вовсе - с переводами строк возможно подумать над конвертацией виддовс<->*никс
не не. от платформы не зависит.
char = unescaped /
escape (
%x22 / ; " quotation mark U+0022
%x5C / ; \ reverse solidus U+005C
%x2F / ; / solidus U+002F
%x62 / ; b backspace U+0008
%x66 / ; f form feed U+000C
%x6E / ; n line feed U+000A
%x72 / ; r carriage return U+000D
%x74 / ; t tab U+0009
%x75 4HEXDIG ) ; uXXXX U+XXXX

Это просто косяк )))
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
не не. от платформы не зависит.
я немного про другое - зависимость в плане отображения, виндяцкое отображение использует (для форматирования) \r\n
это форматирование на вывод в блокнотах разных и т.п.
если копировать и переносить информацию из/в визуальные ср-ва учитывать будет нужно
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
@VladSh
чёт не втупляю, но мну пришлось закоментить
Visual Basic:
 Private Function findElementString(jsonString As String, index As Long) As String
...
         If bRecover Then
            Call recoveryString(value)
        Else
            'If InStr(value, "\") <> 0 Then Call recoveryString(value)
        End If
...
это неправильно, но иначе мне портило путь к темпам
C:\\Program Files\\Apache Software Foundation\\Tomcat 9.0\\temp\\out9035214262981997167.pdf
меняло на табуляцию согласно Sub recoveryString(sValue As String)
но есть подозрение - что-то надо тщательнее продумать ;)
 
Последнее редактирование:

garrick

Lotus Team
26.10.2009
1 367
152
BIT
353
@VladSh
чёт не втупляю, но мну пришлось закоментить
Visual Basic:
 Private Function findElementString(jsonString As String, index As Long) As String
...
         If bRecover Then
            Call recoveryString(value)
        Else
            'If InStr(value, "\") <> 0 Then Call recoveryString(value)
        End If
...
это неправильно, но иначе мне портило путь к темпам
C:\\Program Files\\Apache Software Foundation\\Tomcat 9.0\\temp\\out9035214262981997167.pdf
меняло на табуляцию согласно Sub recoveryString(sValue As String)
но есть подозрение - что-то надо тщательнее продумать ;)
Не пробовали в пути использовать прямой слэш "/"? Работает везде и на Linux и на Windows, и экранировать не надо.
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
Юзаю ralfstx/minimal-json
все устраивает.
для LS2J наваял враппер типа:
Java:
//https://github.com/ralfstx/minimal-json
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;

//import com.eclipsesource.json.JsonObject.Member;

public class RinsJson {
    public JsonObject jObject;

    private RinsJson() { //
        parse("{}");
    }

    public void parse(String jstr) {
        jObject = Json.parse(jstr).asObject();
    }

    // @Override
    public String toString() {
        return jObject.toString();
    }

    public void setArrayString(String key, String values[]) {
        JsonArray jArray = Json.array(values);
        JsonValue jValue = new JsonArray(jArray);
        jObject.set(key, jValue);
    }

    public void addArrayString(String key, String values[]) {
        JsonArray jArray = Json.array(values);
        JsonValue jValue = new JsonArray(jArray);
        jObject.add(key, jValue);
    }

    public String[] getArrayString(String key) {
        String[] retnull = new String[1];
        retnull[0] = "";
        if (jObject.get(key) != null) {
            JsonArray jArray = jObject.get(key).asArray();
            int size = jArray.size();
            if (size == 0) {
                return retnull;
            }
            ;
            String[] ret = new String[size];
            for (int k = 0; k < size; k++) {
                if (jArray.get(k).isString()) {
                    ret[k] = jArray.get(k).asString();
                } else {
                    ret[k] = jArray.get(k).toString();
                }
            }
            return ret;
        } else {
            return retnull;
        }
    }

    public void addObject(String key, String value) {
        JsonObject jObj = Json.parse(value).asObject();
        jObject.add(key, jObj);

    }
  
    public void addArray(String key, String value) {
        JsonArray jObj = Json.parse(value).asArray();
        jObject.add(key, jObj);
    }

    public void setNumber(String key, double value) {
        jObject.set(key, value);
    }

    public void addNumber(String key, double value) {
        jObject.add(key, value);
    }
  
    public JsonValue get(String key) {
        return jObject.get(key);
    }

    public JsonObject getObject() {
        return jObject;
    }
    public boolean isKeyExist(String key) {
        return (jObject.get(key) != null);
    }

    public int getValueType(String key) {
        JsonValue value = jObject.get(key);
        if (value.isArray()) {
            return 1;
        }
        ; // isArray
        if (value.isBoolean()) {
            return 2;
        }
        ; // isBoolean
        if (value.isNumber()) {
            return 3;
        }
        ; // isNumber
        if (value.isString()) {
            return 4;
        }
        ; // isString
        if (value.isObject()) {
            return 5;
        }
        ; // isObject
        return -1;
    }

    public String[] getKeyNames() {
//        if (jObject.names().size() == 0) {
//            String[] ret = { "" };
//            return (ret);
//        }
//        ;
//        return jObject.names().toArray(new String[0]);
        int size=jObject.names().size();
        if (size == 0) {
            String[] retnull = new String[1];
            retnull[0] = "";         
            return (retnull);
            };
        String[] ret = new String[size];
        for (int k = 0; k < size; k++) {
            ret[k]=jObject.names().get(k).toString();
        }
        return ret;
    }

    // @Override
    protected void finalize() throws Throwable {
        // super.finalize();
        jObject = null;
    }
}
а LS2J смогет без рефлексии этим пользоваться? Объект вроди как не получить, а все мемберы не статические...
и ещё момент
Java:
    public void setNumber(String key, double value) {
        jObject.set(key, value);
    }

    public void addNumber(String key, double value) {
        jObject.add(key, value);
    }
из нотусни передача будет float (понятии java) там нет большей точности, а если cuurency надо мутить что-то с Long
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
Не пробовали в пути использовать прямой слэш "/"? Работает везде и на Linux и на Windows, и экранировать не надо.
яж использую внешний сервис (томкат) и как он выдаст - так я и получаю
в данном случае томкат на винде (я уже описывал схему - локальный томкат с нотуснёй рядом)
джава код здесь lmike-mnc/AsposeDocxProcess
еще быстрофиксом вижу такое:
Visual Basic:
        Dim v(1)As String, t(1) As String
v(0)=BSH &{b}:t(0)=Chr(8)
v(1)=BSH &v(0):t(1)=v(1)
        sValue = Replace(sValue, v, t)
v(0)=BSH &{b}:t(0)=Chr(9)
v(1)=BSH &v(0):t(1)=v(1)
        sValue = Replace(sValue, v, t)
....
 
Последнее редактирование:

Domino-Designer

Людям надо поморгать!
Lotus Team
06.12.2011
617
223
BIT
24
Есть и без java
Я спер где то на VB очень грамотные класы по разбору, перевел в LS. Ништяк работает. Или работало. Контактов не осталось.
Идея понятна?
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
Есть и без java
Я спер где то на VB очень грамотные класы по разбору, перевел в LS. Ништяк работает. Или работало. Контактов не осталось.
Идея понятна?
оно понятно, но без кода это тяжело оценивать :)
 
  • Нравится
Реакции: VladSh

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
466
оно понятно, но без кода это тяжело оценивать :)
дык кодже прям тамже
фактически меняем \b:\\b: на Chr(8):\b , хотя как сделает Replace я ещё не знаю, может что-то неправильное сотворить... ;)
возможно надо в два прохода
смысл, например, комбинацию \\b сделать как \b
мб
v(1)=BSH &t(0):t(1)=v(1)
 
Последнее редактирование:

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
232
@lmike
У меня была идея автоматизировать преобразование значений, в которых приходит дата в определённом установленном формате, в поле с типом DateTime, но отказался от этой идеи, т.к. это не всегда нужно. В принципе я всегда знаю, какие поля мне нужно преобразовывать, а какие нет.
Вот и здесь я предлагаю не править общий код, а откорректировать пути уже после преобразования - заменить один слеш на два, если это нужно. Код могу поискать, который производит замену, учитывая количество вхождений.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!