#213332 - 2018-06-05 02:13 AM
JSON parsing
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
Does anyone have any ideas about parsing JSON response strings in Kix?
The JScript libraries that I've looked at don't seem to provide what I'm looking for, or maybe I'm just misunderstanding how this works.
I get one of four data formats as a response -
- A Status response with 3 values in 1 section,
- A Data response with the same status data plus a data section with 1+ values,
- An Array response, again with the same status data, plus 1 or more sets of data values,
- A "complex" response that has the status and data sections, with one or more sub-sections, some of which can contain arrays of data sets.
The standard JSON library allows me to specify a section[:subsection:]Value and get the data for that one value. This seems like an awfully difficult way to parse the response. I'd like to get a single element (by name), a "business record" (array) - either the entire status or data record, a specific data record in an array of records, or - for an array response, a list of data fields for a specific matching field.
Any ideas would be appreciated!
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
#213339 - 2018-06-05 01:33 PM
Re: JSON parsing
[Re: Glenn Barnas]
|
BradV
Seasoned Scripter
Registered: 2006-08-16
Posts: 686
Loc: Maryland, USA
|
Hi Glenn,
Not in kixtart, but powershell could be the answer. ConvertFrom-JSON. I know in Linux python or perl are the recommended ways to manipulate JSON.
Regards,
Brad
|
Top
|
|
|
|
#213349 - 2018-06-07 04:08 PM
Re: JSON parsing
[Re: BradV]
|
Glenn Barnas
KiX Supporter
Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
|
It's part of a proprietary application, so I can't post it fully, but the gist of the solution is converting the JSON input into a two-dimensional array, then parsing that array for a match.
Typical JSON is a collection of objects, referenced as "section.subsection.index.item" - subsection and index are optional and there can be multiple levels of subsections. "Index" represents an element in a sub-collection (an array of items) and can occur at any level.
Parsing the response, I turn it into a string in the first index of a 2-dimensional array - "root.computer.#.hostname", with the data in the second index - "My PC". I can then scan the array for an "object reference", which will return the data from the second index if the request is matched in the first index. - it returns a simple string with the data.
I can also make a request like "root.computer.*.hostname" and it will return a simple array of all the hostnames in the collection. It's smart enough to not match "root.computer.site2.*.hostname".
Finally, I can request the object reference "root.computer.3." - this will return a 2-dimensional array of Value:Data pairs - an entire business record - field name in the 0 index and data in the 1 index per row.
I don't think it can be considered "true" JSON parsing, but it gives me what I need in my application. I'm going to use a different function to return the structure, no data, if that becomes necessary. The part I struggled with is creating a true object reference. Once I saw how PowerShell was parsing it, I was able to develop a reasonable alternative.
Glenn
_________________________
Actually I am a Rocket Scientist!
|
Top
|
|
|
|
#213352 - 2018-06-07 09:52 PM
Re: JSON parsing
[Re: Glenn Barnas]
|
Arend_
MM club member
Registered: 2005-01-17
Posts: 1895
Loc: Hilversum, The Netherlands
|
|
Top
|
|
|
|
#213982 - 2021-03-22 05:34 AM
Re: JSON parsing
[Re: Arend_]
|
AndreLuiz
Getting the hang of it
Registered: 2015-10-07
Posts: 89
Loc: Brasil, João pessoa
|
Sorry to be reliving an old post.
But I had found these vbs-json mentioned above these days, and I decided to try to use it in KiXtart, and working with it pure is a little bad, due to the arrays, it gives an error of expression.
But then I generated a simple function to manipulate the dictionary. In conclusion, for reading Json, it works very well, all that is missing is an easy way to change the data contained in the dictionary.
If you want to modify the script, you can feel free.
Break on
$=SetOption(Explicit, on)
Global $__JsonClass__[1]
Gosub Instance
Main() quit 0
Function Main()
Dim $JsonLocal
$JsonLocal='
{
"test1": "ola",
"test2": {
"hello": "world"
},
"Peoples": [
{
"Name": "Andre",
"Age": 23,
"Types": [
{
"Value": 0
},
{
"Value": 1
},
{
"Value": 2.3
}
]
},
{
"Name": "Milca",
"Age": 25,
"Types": [
{
"Value": true
},
{
"Value": null
},
{
"Value": [
{
"v": "Esse é o valor 1"
},
{
"v": "Esse é o valor 2"
}
]
}
]
}
]
}
'
dim $j, $t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8, $t9
$j = JsonDecode($JsonLocal)
????
$t1 = #($j, 'test1')
$t1'('VarTypeName($t1)')'?
$t2 = #($j, 'test2.hello')?
$t2'('VarTypeName($t2)')'?
$t3 = #($j, 'Peoples[1].Name')?
$t3'('VarTypeName($t3)')'?
$t4 = #($j, 'Peoples[1].Types[0].Value')?
$t4'('VarTypeName($t4)')'?
$t5 = #($j, 'Peoples[1].Types[1].Value')?
$t5'('VarTypeName($t5)')'?
$t6 = #($j, 'Peoples[1].Types[2].Value[0].v')?
$t6'('VarTypeName($t6)')'?
$t7 = #($j, 'Peoples[1].Types[2].Value[1].v')?
$t7'('VarTypeName($t7)')'?
$t8 = #($j, 'Peoples[0].Age')?
$t8'('VarTypeName($t8)')'?
$t9 = #($j, 'Peoples[0].Types[2].Value')?
$t9'('VarTypeName($t9)')'?
Get$
EndFunction
Function JsonDecode($JsonStr)
$JsonDecode = $__JsonClass__[1].Decode($JsonStr)
EndFunction
Function JsonEncode($Obj)
$JsonEncode = $__JsonClass__[1].Encode($Obj)
EndFunction
Function #($ObjectJson, optional $Path)
Dim $Prop, $Propertys, $PartProperty[1]
$Propertys = split($Path, '.')
$PartProperty[0] = subStr($Propertys[0], 1, inStr($Propertys[0], '[')-1)
$PartProperty[0] = iIf(trim($PartProperty[0]) = '', $Propertys[0],subStr($Propertys[0], 1, inStr($Propertys[0], '[')-1))
$PartProperty[1] = subStr($Propertys[0], inStr($Propertys[0], '['))
$# = $ObjectJson.item($PartProperty[0])
$Path = join($Propertys, '.')
$Path = subStr($Path, inStr($Path, '.')+1)
If (varTypeName($#) = "Variant[]")
$=execute('$# = $#'+$PartProperty[1])
EndIf
If (varTypeName($#) = "Object")
$# = #($#, $Path)
EndIf
EndFunction
:Instance
$__JsonClass__[0] = CreateObject("ScriptControl")
$__JsonClass__[0].Language = "VBScript"
$__JsonClass__[0].AddCode(
'Class VbsJson
REM Author: Demon
REM Date: 2012/5/3
REM Website: http://demon.tw
Private Whitespace, NumberRegex, StringChunk
Private b, f, r, n, t
Private Sub Class_Initialize
Whitespace = " " & vbTab & vbCr & vbLf
b = ChrW(8)
f = vbFormFeed
r = vbCr
n = vbLf
t = vbTab
Set NumberRegex = New RegExp
NumberRegex.Pattern = "(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?"
NumberRegex.Global = False
NumberRegex.MultiLine = True
NumberRegex.IgnoreCase = True
Set StringChunk = New RegExp
StringChunk.Pattern = "([\s\S]*?)([""\\\x00-\x1f])"
StringChunk.Global = False
StringChunk.MultiLine = True
StringChunk.IgnoreCase = True
End Sub
REM Return a JSON string representation of a VBScript data structure
REM Supports the following objects and types
REM +-------------------+---------------+
REM | VBScript | JSON |
REM +===================+===============+
REM | Dictionary | object |
REM +-------------------+---------------+
REM | Array | array |
REM +-------------------+---------------+
REM | String | string |
REM +-------------------+---------------+
REM | Number | number |
REM +-------------------+---------------+
REM | True | true |
REM +-------------------+---------------+
REM | False | false |
REM +-------------------+---------------+
REM | Null | null |
REM +-------------------+---------------+
Public Function Encode(ByRef obj)
Dim buf, i, c, g
Set buf = CreateObject("Scripting.Dictionary")
Select Case VarType(obj)
Case vbNull
buf.Add buf.Count, "null"
Case vbBoolean
If obj Then
buf.Add buf.Count, "true"
Else
buf.Add buf.Count, "false"
End If
Case vbInteger, vbLong, vbSingle, vbDouble
buf.Add buf.Count, Replace(obj, ",", ".")
Case vbString
buf.Add buf.Count, """"
For i = 1 To Len(obj)
c = Mid(obj, i, 1)
Select Case c
Case """" buf.Add buf.Count, "\"""
Case "\" buf.Add buf.Count, "\\"
Case "/" buf.Add buf.Count, "/"
Case b buf.Add buf.Count, "\b"
Case f buf.Add buf.Count, "\f"
Case r buf.Add buf.Count, "\r"
Case n buf.Add buf.Count, "\n"
Case t buf.Add buf.Count, "\t"
Case Else
If AscW(c) >= 0 And AscW(c) <= 31 Then
c = Right("0" & Hex(AscW(c)), 2)
buf.Add buf.Count, "\u00" & c
Else
buf.Add buf.Count, c
End If
End Select
Next
buf.Add buf.Count, """"
Case vbArray + vbVariant
g = True
buf.Add buf.Count, "["
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, Encode(i)
Next
buf.Add buf.Count, "]"
Case vbObject
If TypeName(obj) = "Dictionary" Then
g = True
buf.Add buf.Count, "{"
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, """" & i & """" & ":" & Encode(obj(i))
Next
buf.Add buf.Count, "}"
Else
Err.Raise 8732,,"None dictionary object"
End If
Case Else
buf.Add buf.Count, """" & CStr(obj) & """"
End Select
Encode = Join(buf.Items, "")
End Function
REM Return the VBScript representation of str(
REM Performs the following translations in decoding
REM +---------------+-------------------+
REM | JSON | VBScript |
REM +===============+===================+
REM | object | Dictionary |
REM +---------------+-------------------+
REM | array | Array |
REM +---------------+-------------------+
REM | string | String |
REM +---------------+-------------------+
REM | number | Double |
REM +---------------+-------------------+
REM | true | True |
REM +---------------+-------------------+
REM | false | False |
REM +---------------+-------------------+
REM | null | Null |
REM +---------------+-------------------+
Public Function Decode(ByRef str)
Dim idx
idx = SkipWhitespace(str, 1)
If Mid(str, idx, 1) = "{" Then
Set Decode = ScanOnce(str, 1)
Else
Decode = ScanOnce(str, 1)
End If
End Function
Private Function ScanOnce(ByRef str, ByRef idx)
Dim c, ms
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "{" Then
idx = idx + 1
Set ScanOnce = ParseObject(str, idx)
Exit Function
ElseIf c = "[" Then
idx = idx + 1
ScanOnce = ParseArray(str, idx)
Exit Function
ElseIf c = """" Then
idx = idx + 1
ScanOnce = ParseString(str, idx)
Exit Function
ElseIf c = "n" And StrComp("null", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = Null
Exit Function
ElseIf c = "t" And StrComp("true", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = True
Exit Function
ElseIf c = "f" And StrComp("false", Mid(str, idx, 5)) = 0 Then
idx = idx + 5
ScanOnce = False
Exit Function
End If
Set ms = NumberRegex.Execute(Mid(str, idx))
If ms.Count = 1 Then
idx = idx + ms(0).Length
ScanOnce = Eval(ms(0))
Exit Function
End If
Err.Raise 8732,,"No JSON object could be ScanOnced"
End Function
Private Function ParseObject(ByRef str, ByRef idx)
Dim c, key, value
Set ParseObject = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Function
ElseIf c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Do
key = ParseString(str, idx)
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) <> ":" Then
Err.Raise 8732,,"Expecting : delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
ParseObject.Add key, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
c = Mid(str, idx, 1)
If c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Loop
idx = idx + 1
End Function
Private Function ParseArray(ByRef str, ByRef idx)
Dim c, values, value
Set values = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
ParseArray = values.Items
Exit Function
End If
Do
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
values.Add values.Count, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = idx + 1
Loop
idx = idx + 1
ParseArray = values.Items
End Function
Private Function ParseString(ByRef str, ByRef idx)
Dim chunks, content, terminator, ms, esc, char
Set chunks = CreateObject("Scripting.Dictionary")
Do
Set ms = StringChunk.Execute(Mid(str, idx))
If ms.Count = 0 Then
Err.Raise 8732,,"Unterminated string starting"
End If
content = ms(0).Submatches(0)
terminator = ms(0).Submatches(1)
If Len(content) > 0 Then
chunks.Add chunks.Count, content
End If
idx = idx + ms(0).Length
If terminator = """" Then
Exit Do
ElseIf terminator <> "\" Then
Err.Raise 8732,,"Invalid control character"
End If
esc = Mid(str, idx, 1)
If esc <> "u" Then
Select Case esc
Case """" char = """"
Case "\" char = "\"
Case "/" char = "/"
Case "b" char = b
Case "f" char = f
Case "n" char = n
Case "r" char = r
Case "t" char = t
Case Else Err.Raise 8732,,"Invalid escape"
End Select
idx = idx + 1
Else
char = ChrW("&H" & Mid(str, idx + 1, 4))
idx = idx + 5
End If
chunks.Add chunks.Count, char
Loop
ParseString = Join(chunks.Items, "")
End Function
Private Function SkipWhitespace(ByRef str, ByVal idx)
Do While idx <= Len(str) And _
InStr(Whitespace, Mid(str, idx, 1)) > 0
idx = idx + 1
Loop
SkipWhitespace = idx
End Function
End Class')
$__JsonClass__[1] = $__JsonClass__[0].Eval('new VbsJson')
Return
|
Top
|
|
|
|
Moderator: Jochen, Allen, Radimus, Glenn Barnas, ShaneEP, Ruud van Velsen, Arend_, Mart
|
1 registered
(Allen)
and 365 anonymous users online.
|
|
|