VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END Attribute VB_Name = "ClsJSONLib" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit ' clsJSONLib taken from https://code.google.com/archive/p/vba-json/ ' Not stated in that source, but according to Tim Hall the original author was: ' Copyright (c) 2013, Ryo Yokoyama, All rights reserved. Private Const INVALID_JSON As Long = 1 Private Const INVALID_OBJECT As Long = 2 Private Const INVALID_ARRAY As Long = 3 Private Const INVALID_BOOLEAN As Long = 4 Private Const INVALID_NULL As Long = 5 Private Const INVALID_KEY As Long = 6 Private Const Debug_Mode As Boolean = False ' TESTING ' ' parse string and create JSON object (Dictionary or Collection in VB) ' Public Function parse(ByRef str As String) As Object '@notrace Dim Index As Long Index = 1 skipChar str, Index Select Case Mid$(str, Index, 1) Case "{" Set parse = parseObject(str, Index) ' Dictionary Case "[" Set parse = parseArray(str, Index) ' Collection Case Else Err.Raise vbObjectError + INVALID_JSON, Description:="char " & Index & " : " & Mid$(str, Index, 1) End Select End Function ' ' parse collection of key/value (Dictionary in VB) ' Private Function parseObject(ByRef str As String, ByRef Index As Long) As Object '@notrace Set parseObject = CreateObject("Scripting.Dictionary") ' requires reference to Microsoft Scripting Runtime ' "{" skipChar str, Index If Mid$(str, Index, 1) <> "{" Then Err.Raise vbObjectError + INVALID_OBJECT, Description:="char " & Index & " : " & Mid$(str, Index) Index = Index + 1 Do skipChar str, Index If "}" = Mid$(str, Index, 1) Then Index = Index + 1 Exit Do ElseIf "," = Mid$(str, Index, 1) Then Index = Index + 1 skipChar str, Index End If ' add key/value pair parseObject.Add key:=parseKey(str, Index), Item:=parseValue(str, Index) Loop If Debug_Mode Then Debug.Print Index & " parseObject.Count " & parseObject.Count End Function ' ' parse list (Collection in VB) ' Private Function parseArray(ByRef str As String, ByRef Index As Long) As Collection '@notrace Set parseArray = New Collection ' "[" skipChar str, Index If Mid$(str, Index, 1) <> "[" Then Err.Raise vbObjectError + INVALID_ARRAY, Description:="char " & Index & " : " + Mid$(str, Index) Index = Index + 1 Do skipChar str, Index If "]" = Mid$(str, Index, 1) Then Index = Index + 1 Exit Do ElseIf "," = Mid$(str, Index, 1) Then Index = Index + 1 skipChar str, Index End If ' add value parseArray.Add parseValue(str, Index) Loop If Debug_Mode Then Debug.Print Index & " parseArray.Count " & parseArray.Count End Function ' ' parse string / number / object / array / true / false / null ' Private Function parseValue(ByRef str As String, ByRef Index As Long) As Variant '@notrace skipChar str, Index Select Case Mid$(str, Index, 1) Case "{" Set parseValue = parseObject(str, Index) Case "[" Set parseValue = parseArray(str, Index) Case """", "'" ' double, single quotes parseValue = parseString(str, Index) Case "t", "f" parseValue = parseBoolean(str, Index) Case "n" parseValue = parseNull(str, Index) Case Else parseValue = parseNumber(str, Index) End Select If Debug_Mode Then Debug.Print Index & " parseValue " & TypeName(parseValue) End Function ' ' parse string ' Private Function parseString(ByRef str As String, ByRef Index As Long) As String '@notrace Dim quote As String Dim Char As String Dim code As String skipChar str, Index quote = Mid$(str, Index, 1) Index = Index + 1 Do While Index > 0 And Index <= Len(str) Char = Mid$(str, Index, 1) Select Case (Char) Case "\" Index = Index + 1 Char = Mid$(str, Index, 1) Select Case (Char) Case """", "\", "/" ' POB fix issue 22 in lib parseString = parseString & Char Index = Index + 1 Case "b" parseString = parseString & vbBack Index = Index + 1 Case "f" parseString = parseString & vbFormFeed Index = Index + 1 Case "n" parseString = parseString & vbNewLine Index = Index + 1 Case "r" parseString = parseString & vbCr Index = Index + 1 Case "t" parseString = parseString & vbTab Index = Index + 1 Case "u" Index = Index + 1 code = Mid$(str, Index, 4) parseString = parseString & ChrW$(Val("&h" + code)) Index = Index + 4 End Select Case quote Index = Index + 1 Exit Function Case Else parseString = parseString & Char Index = Index + 1 End Select Loop End Function ' ' parse number ' Private Function parseNumber(ByRef str As String, ByRef Index As Long) As Double '@notrace Dim value As String Dim Char As String skipChar str, Index Do While Index > 0 And Index <= Len(str) Char = Mid$(str, Index, 1) If InStr("+-0123456789.eE", Char) Then value = value & Char Index = Index + 1 Else parseNumber = Val(value) Exit Function End If Loop End Function ' ' parse true / false ' Private Function parseBoolean(ByRef str As String, ByRef Index As Long) As Boolean '@notrace skipChar str, Index If Mid$(str, Index, 4) = "true" Then parseBoolean = True Index = Index + 4 ElseIf Mid$(str, Index, 5) = "false" Then parseBoolean = False Index = Index + 5 Else Err.Raise vbObjectError + INVALID_BOOLEAN, Description:="char " & Index & " : " & Mid$(str, Index) End If End Function ' ' parse null ' Private Function parseNull(ByRef str As String, ByRef Index As Long) As Variant '@notrace skipChar str, Index If Mid$(str, Index, 4) = "null" Then parseNull = Null Index = Index + 4 Else Err.Raise vbObjectError + INVALID_NULL, Description:="char " & Index & " : " & Mid$(str, Index) End If End Function Private Function parseKey(ByRef str As String, ByRef Index As Long) As String '@notrace Dim dquote As Boolean Dim sQuote As Boolean Dim Char As String skipChar str, Index Do While Index > 0 And Index <= Len(str) Char = Mid$(str, Index, 1) Select Case (Char) Case """" dquote = Not dquote Index = Index + 1 If Not dquote Then skipChar str, Index If Mid$(str, Index, 1) <> ":" Then Err.Raise vbObjectError + INVALID_KEY, Description:="char " & Index & " : " & parseKey End If End If Case "'" sQuote = Not sQuote Index = Index + 1 If Not sQuote Then skipChar str, Index If Mid$(str, Index, 1) <> ":" Then Err.Raise vbObjectError + INVALID_KEY, Description:="char " & Index & " : " & parseKey End If End If Case ":" If Not dquote And Not sQuote Then Index = Index + 1 Exit Do End If Case Else If InStr(vbCr & vbLf & vbTab & " ", Char) = 0 Then parseKey = parseKey & Char End If Index = Index + 1 End Select Loop If Debug_Mode Then Debug.Print Index & " parseKey=" & parseKey End Function ' ' skip special character ' Friend Sub skipChar(ByRef str As String, ByRef Index As Long) '@notrace ' Dev:Friend; should be Private for prod While Index > 0 And Index <= Len(str) And InStr(vbCr & vbLf & vbTab & " ", Mid$(str, Index, 1)) '4.27 POB don't need vbCrLf as well Index = Index + 1 Wend End Sub Public Function toString(ByRef obj As Variant) As String '@notrace Select Case VarType(obj) Case vbNull toString = "null" Case vbDate toString = """" & CStr(obj) & """" Case vbString toString = """" & encode(obj) & """" Case vbObject Dim bFI As Boolean, i As Long bFI = True If TypeName(obj) = "Dictionary" Then toString = toString & "{" Dim keys keys = obj.keys For i = 0 To obj.Count - 1 If bFI Then bFI = False Else toString = toString & "," End If Dim key key = keys(i) toString = toString & """" & key & """:" & toString(obj(key)) Next i toString = toString & "}" ElseIf TypeName(obj) = "Collection" Then toString = toString & "[" Dim value For Each value In obj If bFI Then bFI = False Else toString = toString & "," toString = toString & toString(value) Next value toString = toString & "]" End If Case vbBoolean If obj Then toString = "true" Else toString = "false" Case vbVariant, vbArray, vbArray + vbVariant Dim sEB As String toString = multiArray(obj, 1, vbNullString, sEB) Case Else toString = Replace(obj, ",", ".") End Select End Function Private Function encode(str) As String '@notrace ' converts text to json character codes like \n \r Dim i As Long, j As Long, aL1 As Variant, aL2 As Variant, c As String, p As Boolean aL1 = Array(&H22, &H5C, &H2F, &H8, &HC, &HA, &HD, &H9) ' " \ / BS FF LF CR TAB ' 34 92 47 8 12 10 13 9 aL2 = Array(&H22, &H5C, &H2F, &H62, &H66, &H6E, &H72, &H74) ' " \ / b f n r t ' 34 92 47 98 102 110 114 116 For i = 1 To Len(str) p = True c = Mid$(str, i, 1) For j = 0 To 7 If c = Chr$(aL1(j)) Then encode = encode & "\" & Chr$(aL2(j)) p = False Exit For End If Next If p Then Dim a As Long a = AscW(c) If a > 31 And a < 127 Then encode = encode & c Else 'was: If a > -1 Or a < 65535 Then If a < 0 Then 'POB handle Asian characters a<0 eg -10179 = 55357 = D83D a = 65536 + a End If encode = encode & "\u" & String(4 - Len(Hex$(a)), "0") & Hex$(a) End If End If Next End Function Private Function multiArray(aBD As Variant, iBC As Long, sPS As String, ByRef sPT As String) As String '@notrace ' Array BoDy, Integer BaseCount, String PoSition Dim iDU As Long, iDL As Long, i As Long ' Integer DimensionUBound, Integer DimensionLBound Dim lErr As Long, sPB1, sPB2 ' String PointBuffer1, String PointBuffer2 On Error Resume Next iDL = LBound(aBD, iBC) iDU = UBound(aBD, iBC) lErr = Err.Number ' error 9 if unallocated array On Error GoTo 0 If lErr <> 0 Then sPB1 = sPT & sPS For i = 1 To Len(sPB1) If i <> 1 Then sPB2 = sPB2 & "," sPB2 = sPB2 & Mid$(sPB1, i, 1) Next multiArray = multiArray & toString(aBD(sPB2)) Else sPT = sPT & sPS multiArray = multiArray & "[" For i = iDL To iDU multiArray = multiArray & multiArray(aBD, iBC + 1, CStr(i), sPT) If i < iDU Then multiArray = multiArray & "," Next multiArray = multiArray & "]" If iBC > 1 Then ' POB fix case when iBC=1 sPT = Left$(sPT, iBC - 2) End If End If End Function