619 lines
19 KiB
OpenEdge ABL
619 lines
19 KiB
OpenEdge ABL
VERSION 1.0 CLASS
|
|
BEGIN
|
|
MultiUse = -1 'True
|
|
END
|
|
Attribute VB_Name = "API_JSON"
|
|
Attribute VB_GlobalNameSpace = False
|
|
Attribute VB_Creatable = False
|
|
Attribute VB_PredeclaredId = False
|
|
Attribute VB_Exposed = False
|
|
' ======== JSON converter ========
|
|
' Shared module version: 20220813
|
|
' Tested in: TestCommons
|
|
' Depends on:
|
|
' Required reference: Scripting
|
|
' This code is based on rethinking VBA-JSON, https://github.com/VBA-tools/VBA-JSON
|
|
Option Explicit
|
|
|
|
Private Const ERR_PARSE_FAIL = vbObject + 2049
|
|
|
|
' JSON states esaping '/' as optional.
|
|
' Use this flag to escape them as '\/'
|
|
Public escapeSlash_ As Boolean
|
|
|
|
' JSON doesnt require escaping of non-AscII chars.
|
|
' Use this flag to enable \u codes escape mechanics
|
|
Public escapeUnicode_ As Boolean
|
|
|
|
Private multiline_ As Boolean
|
|
Private wsCharacter_ As String
|
|
Private wsCount_ As Long
|
|
|
|
Private json_ As String
|
|
Private inputLen_ As Long
|
|
Private position_ As Long
|
|
|
|
Private Sub Class_Initialize()
|
|
escapeSlash_ = False
|
|
escapeUnicode_ = False
|
|
Call DisableMultiline
|
|
End Sub
|
|
|
|
' {Integer|String} vWhitespace "Pretty" print json with given number of spaces per indentation (Integer) or given string
|
|
Public Function SetupMultiline(ByVal vWhitespace As Variant)
|
|
multiline_ = True
|
|
If VBA.VarType(vWhitespace) = VBA.vbString Then
|
|
wsCharacter_ = vWhitespace
|
|
wsCount_ = -1
|
|
Else
|
|
wsCharacter_ = vbNullString
|
|
wsCount_ = vWhitespace
|
|
End If
|
|
End Function
|
|
|
|
Public Function DisableMultiline()
|
|
multiline_ = False
|
|
wsCharacter_ = vbNullString
|
|
wsCount_ = -1
|
|
End Function
|
|
|
|
' Convert object (Dictionary/Collection/Array) to JSON
|
|
Public Function CreateJSON(ByRef vValue As Variant) As String
|
|
CreateJSON = Generator_Process(vValue)
|
|
End Function
|
|
|
|
' Convert JSON string to object (Dictionary/Collection)
|
|
Public Function Parse(sJson$) As Object
|
|
json_ = VBA.Replace(VBA.Replace(VBA.Replace(sJson, VBA.vbCr, ""), VBA.vbLf, ""), VBA.vbTab, "")
|
|
position_ = 1
|
|
inputLen_ = VBA.Len(json_)
|
|
|
|
Call Parser_SkipWS
|
|
|
|
Select Case Parser_ViewAny()
|
|
Case "{": Set Parse = Parser_Object()
|
|
Case "[": Set Parse = Parser_Array()
|
|
Case Else: Call Parser_RaiseError("Expected '{' or '['")
|
|
End Select
|
|
|
|
If position_ <= inputLen_ Then _
|
|
Call Parser_RaiseError("Some part of input is out of main object scope")
|
|
|
|
json_ = vbNullString
|
|
End Function
|
|
|
|
Public Function SaveToFile(sFile$, iData As Object) As Boolean
|
|
Const DO_OVERWRITE& = 2
|
|
|
|
SaveToFile = False
|
|
On Error GoTo EXIT_ERROR
|
|
|
|
Dim adoStream As Object: Set adoStream = CreateObject("ADODB.Stream")
|
|
adoStream.Charset = "utf-8"
|
|
Call adoStream.Open
|
|
Call adoStream.WriteText(CreateJSON(iData))
|
|
Call adoStream.SaveToFile(sFile, DO_OVERWRITE)
|
|
|
|
SaveToFile = True
|
|
|
|
EXIT_ERROR:
|
|
On Error GoTo 0
|
|
Call adoStream.Close
|
|
End Function
|
|
|
|
Public Function LoadFromFile(sFile$) As Object
|
|
Dim sJson$: sJson = ReadFile(sFile)
|
|
If sJson = vbNullString Then _
|
|
Exit Function
|
|
Set LoadFromFile = Parse(sJson)
|
|
End Function
|
|
|
|
' =============================================
|
|
Private Function ReadFile(sFile$) As String
|
|
Dim adoStream As Object: Set adoStream = CreateObject("ADODB.Stream")
|
|
adoStream.Charset = "utf-8"
|
|
Call adoStream.Open
|
|
|
|
On Error GoTo ERROR_FILE
|
|
Call adoStream.LoadFromFile(sFile)
|
|
On Error GoTo 0
|
|
|
|
ReadFile = adoStream.ReadText
|
|
|
|
ERROR_FILE:
|
|
Call adoStream.Close
|
|
On Error GoTo 0
|
|
End Function
|
|
|
|
Private Function Generator_Process(ByRef vValue As Variant, Optional nIdent& = 0) As String
|
|
Select Case VBA.VarType(vValue)
|
|
Case VBA.vbNull: Generator_Process = "null"
|
|
Case VBA.vbBoolean: Generator_Process = IIf(vValue, "true", "false")
|
|
Case VBA.vbDate: Generator_Process = """" & CDbl(vValue) & """"
|
|
Case VBA.vbString
|
|
If IsLargeNumber(vValue) Then
|
|
Generator_Process = vValue ' Large number encoded as string
|
|
Else
|
|
Generator_Process = """" & EncodeStr(vValue) & """"
|
|
End If
|
|
|
|
Case VBA.vbArray To VBA.vbArray + VBA.vbByte: Generator_Process = Generator_Array(vValue, nIdent)
|
|
|
|
Case VBA.vbObject
|
|
If VBA.TypeName(vValue) = "Dictionary" Then
|
|
Generator_Process = Generator_Dictionary(vValue, nIdent)
|
|
ElseIf VBA.TypeName(vValue) = "Collection" Then
|
|
Generator_Process = Generator_Collection(vValue, nIdent)
|
|
Else ' TODO: call custom object function to generate JSON!
|
|
Call Err.Raise(1000, Description:="Unknown object type. Please use Dictionary or Collection")
|
|
End If
|
|
|
|
Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal
|
|
Generator_Process = VBA.Replace(vValue, ",", ".") ' Number (use decimals for numbers)
|
|
Case Else ' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType
|
|
On Error Resume Next
|
|
Generator_Process = vValue
|
|
On Error GoTo 0
|
|
End Select
|
|
End Function
|
|
|
|
Private Function Generator_Array(ByRef vValue As Variant, nIdent&) As String
|
|
Dim sBuffer$
|
|
Dim nBufferPosition&
|
|
Dim nBufferLen&
|
|
Call BufferAppend(sBuffer, "[", nBufferPosition, nBufferLen)
|
|
|
|
Dim nSize1&: nSize1 = ArraySize(vValue, 1)
|
|
If nSize1 <= 0 Then _
|
|
GoTo PRINT_TO_STR
|
|
Dim nIndex1Start&: nIndex1Start = LBound(vValue, 1)
|
|
|
|
Dim nSize2&: nSize2 = ArraySize(vValue, 2)
|
|
Dim nIndex2Start&
|
|
If nSize2 > 0 Then _
|
|
nIndex2Start = LBound(vValue, 2)
|
|
|
|
Dim sIndentLevel1$: sIndentLevel1 = GetWS(nIdent + 1)
|
|
Dim sIndentLevel2$: sIndentLevel2 = GetWS(nIdent + 2)
|
|
|
|
Dim bIsFirst: bIsFirst = True
|
|
Dim json_Converted$
|
|
Dim nIndex1&
|
|
For nIndex1 = nIndex1Start To nIndex1Start + nSize1 - 1 Step 1
|
|
If Not bIsFirst Then
|
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
|
Else
|
|
bIsFirst = False
|
|
End If
|
|
|
|
If nSize2 <= 0 Then ' 1D Array
|
|
json_Converted = Generator_Nullable(vValue(nIndex1), nIdent + 1)
|
|
If multiline_ Then _
|
|
json_Converted = vbNewLine & sIndentLevel1 & json_Converted
|
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
|
Else ' 2D Array
|
|
If multiline_ Then _
|
|
Call BufferAppend(sBuffer, vbNewLine, nBufferPosition, nBufferLen)
|
|
Call BufferAppend(sBuffer, sIndentLevel1 & "[", nBufferPosition, nBufferLen)
|
|
|
|
Dim nIndex2&
|
|
Dim bIsFirst2 As Boolean: bIsFirst2 = True
|
|
For nIndex2 = nIndex2Start To nIndex2Start + nSize2 - 1 Step 1
|
|
If Not bIsFirst2 Then
|
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
|
Else
|
|
bIsFirst2 = False
|
|
End If
|
|
json_Converted = Generator_Nullable(vValue(nIndex1, nIndex2), nIdent + 2)
|
|
If multiline_ Then _
|
|
json_Converted = vbNewLine & sIndentLevel2 & json_Converted
|
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
|
Next nIndex2
|
|
|
|
If multiline_ Then _
|
|
Call BufferAppend(sBuffer, vbNewLine, nBufferPosition, nBufferLen)
|
|
Call BufferAppend(sBuffer, sIndentLevel1 & "]", nBufferPosition, nBufferLen)
|
|
End If
|
|
Next nIndex1
|
|
|
|
PRINT_TO_STR:
|
|
On Error GoTo 0
|
|
|
|
If multiline_ Then
|
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "]", nBufferPosition, nBufferLen)
|
|
Else
|
|
Call BufferAppend(sBuffer, "]", nBufferPosition, nBufferLen)
|
|
End If
|
|
Generator_Array = BufferToString(sBuffer, nBufferPosition)
|
|
End Function
|
|
|
|
Private Function Generator_Collection(ByRef vValue As Variant, nIdent&) As String
|
|
Dim sBuffer$
|
|
Dim nBufferPosition&
|
|
Dim nBufferLen&
|
|
Call BufferAppend(sBuffer, "[", nBufferPosition, nBufferLen)
|
|
|
|
Dim bIsFirst As Boolean: bIsFirst = True
|
|
Dim anElement As Variant
|
|
For Each anElement In vValue
|
|
If Not bIsFirst Then
|
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
|
Else
|
|
bIsFirst = False
|
|
End If
|
|
|
|
Dim json_Converted$: json_Converted = Generator_Nullable(anElement, nIdent + 1)
|
|
If multiline_ Then _
|
|
json_Converted = vbNewLine & GetWS(nIdent + 1) & json_Converted
|
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
|
Next anElement
|
|
|
|
If multiline_ Then
|
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "]", nBufferPosition, nBufferLen)
|
|
Else
|
|
Call BufferAppend(sBuffer, "]", nBufferPosition, nBufferLen)
|
|
End If
|
|
Generator_Collection = BufferToString(sBuffer, nBufferPosition)
|
|
End Function
|
|
|
|
Private Function Generator_Dictionary(ByRef vValue As Variant, nIdent&) As String
|
|
Dim sBuffer$
|
|
Dim nBufferPosition&
|
|
Dim nBufferLen&
|
|
Call BufferAppend(sBuffer, "{", nBufferPosition, nBufferLen)
|
|
|
|
Dim bIsFirst As Boolean: bIsFirst = True
|
|
Dim aKey As Variant
|
|
For Each aKey In vValue.Keys
|
|
Dim json_Converted$: json_Converted = Generator_Process(vValue(aKey), nIdent + 1)
|
|
If json_Converted = vbNullString Then _
|
|
If IsUndefined(vValue(aKey)) Then _
|
|
GoTo NEXT_KEY ' For Objects, undefined (Empty/Nothing) is not added to object
|
|
|
|
If Not bIsFirst Then
|
|
Call BufferAppend(sBuffer, ",", nBufferPosition, nBufferLen)
|
|
Else
|
|
bIsFirst = False
|
|
End If
|
|
|
|
If multiline_ Then
|
|
json_Converted = vbNewLine & GetWS(nIdent + 1) & """" & aKey & """: " & json_Converted
|
|
Else
|
|
json_Converted = """" & aKey & """:" & json_Converted
|
|
End If
|
|
|
|
Call BufferAppend(sBuffer, json_Converted, nBufferPosition, nBufferLen)
|
|
NEXT_KEY:
|
|
Next aKey
|
|
|
|
If multiline_ Then
|
|
Call BufferAppend(sBuffer, vbNewLine & GetWS(nIdent) & "}", nBufferPosition, nBufferLen)
|
|
Else
|
|
Call BufferAppend(sBuffer, "}", nBufferPosition, nBufferLen)
|
|
End If
|
|
Generator_Dictionary = BufferToString(sBuffer, nBufferPosition)
|
|
End Function
|
|
|
|
Private Function Generator_Nullable(ByVal vValue As Variant, nIdent&)
|
|
Generator_Nullable = Generator_Process(vValue, nIdent)
|
|
If Generator_Nullable = vbNullString Then _
|
|
If IsUndefined(vValue) Then _
|
|
Generator_Nullable = "null"
|
|
End Function
|
|
|
|
Private Function Parser_Object() As Scripting.Dictionary
|
|
If Parser_ConsumeChar() <> "{" Then _
|
|
Call Parser_RaiseError("Expected '{'")
|
|
|
|
Set Parser_Object = New Scripting.Dictionary
|
|
Do
|
|
Call Parser_SkipWS
|
|
Dim sNextChar$: sNextChar = Parser_ViewChar()
|
|
If sNextChar = "}" Then
|
|
Call Parser_Increment
|
|
Exit Function
|
|
ElseIf sNextChar = "," Then
|
|
Call Parser_Increment
|
|
Call Parser_SkipWS
|
|
End If
|
|
|
|
Dim sKey$: sKey = Parser_Key()
|
|
If Parser_ConsumeChar(1) <> ":" Then _
|
|
Call Parser_RaiseError("Expected ':'")
|
|
sNextChar = Parser_ViewChar()
|
|
If sNextChar = "[" Or sNextChar = "{" Then
|
|
Set Parser_Object.Item(sKey) = Parser_Value()
|
|
Else
|
|
Parser_Object.Item(sKey) = Parser_Value()
|
|
End If
|
|
Loop
|
|
End Function
|
|
|
|
Private Function Parser_Array() As Collection
|
|
If Parser_ConsumeChar() <> "[" Then _
|
|
Call Parser_RaiseError("Expected '['")
|
|
|
|
Set Parser_Array = New Collection
|
|
Do
|
|
Call Parser_SkipWS
|
|
Dim sChar$: sChar = Parser_ViewChar(1)
|
|
If sChar = "]" Then
|
|
Call Parser_Increment
|
|
Exit Function
|
|
ElseIf sChar = "," Then
|
|
Call Parser_Increment
|
|
End If
|
|
Call Parser_Array.Add(Parser_Value())
|
|
Loop
|
|
End Function
|
|
|
|
Private Function Parser_Value() As Variant
|
|
Call Parser_SkipWS
|
|
Dim sValue$: sValue = Parser_ViewAny(1)
|
|
Select Case sValue
|
|
Case "{": Set Parser_Value = Parser_Object()
|
|
Case "[": Set Parser_Value = Parser_Array()
|
|
Case """", "'": Parser_Value = Parser_String()
|
|
Case Else
|
|
If VBA.InStr("+-0123456789", sValue) Then
|
|
Parser_Value = Parser_Numbers()
|
|
Exit Function
|
|
End If
|
|
|
|
If inputLen_ - position_ + 1 > 4 Then
|
|
sValue = Parser_ViewAny(4)
|
|
If sValue = "true" Then
|
|
Parser_Value = True
|
|
Call Parser_Increment(4)
|
|
Exit Function
|
|
ElseIf sValue = "null" Then
|
|
Parser_Value = Null
|
|
Call Parser_Increment(4)
|
|
Exit Function
|
|
End If
|
|
End If
|
|
|
|
If inputLen_ - position_ + 1 > 5 Then
|
|
sValue = Parser_ViewAny(5)
|
|
If sValue = "false" Then
|
|
Parser_Value = False
|
|
Call Parser_Increment(5)
|
|
Exit Function
|
|
End If
|
|
End If
|
|
|
|
Call Parser_RaiseError("Expected 'STRING', 'NUMBER', null, true, false, '{', or '['")
|
|
End Select
|
|
End Function
|
|
|
|
Private Function Parser_String() As String
|
|
Dim sBuffer$
|
|
Dim nBufferPosition&
|
|
Dim nBufferLen&
|
|
|
|
Dim sChar$
|
|
Dim json_Quote$: json_Quote = Parser_ConsumeChar()
|
|
Do While position_ <= inputLen_
|
|
sChar = Parser_ConsumeAny(1)
|
|
If sChar = json_Quote Then
|
|
Parser_String = BufferToString(sBuffer, nBufferPosition)
|
|
Exit Function
|
|
ElseIf sChar = "\" Then
|
|
' Escaped string, \\, or \/
|
|
sChar = Parser_ConsumeAny(1)
|
|
Select Case sChar
|
|
Case """", "\", "/", "'": Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
|
Case "b": Call BufferAppend(sBuffer, vbBack, nBufferPosition, nBufferLen)
|
|
Case "f": Call BufferAppend(sBuffer, vbFormFeed, nBufferPosition, nBufferLen)
|
|
Case "n": Call BufferAppend(sBuffer, vbCrLf, nBufferPosition, nBufferLen)
|
|
Case "r": Call BufferAppend(sBuffer, vbCr, nBufferPosition, nBufferLen)
|
|
Case "t": Call BufferAppend(sBuffer, vbTab, nBufferPosition, nBufferLen)
|
|
Case "u"
|
|
Dim sUTF8Char$: sUTF8Char = Parser_ViewAny(4)
|
|
Call BufferAppend(sBuffer, VBA.ChrW(VBA.val("&h" + sUTF8Char)), nBufferPosition, nBufferLen)
|
|
Call Parser_Increment(4)
|
|
Case Else: Call Parser_Increment(-1)
|
|
End Select
|
|
Else
|
|
Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
|
End If
|
|
Loop
|
|
End Function
|
|
|
|
Private Function Parser_Numbers() As Variant
|
|
Call Parser_SkipWS
|
|
Dim sChar$
|
|
Dim sValue$: sValue = vbNullString
|
|
Do While position_ <= inputLen_
|
|
sChar = Parser_ViewAny(1)
|
|
If VBA.InStr("+-0123456789.eE", sChar) Then
|
|
sValue = sValue & sChar
|
|
Call Parser_Increment(1)
|
|
Else
|
|
' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number
|
|
Dim bIsLarge As Boolean: bIsLarge = IIf(VBA.InStr(sValue, "."), VBA.Len(sValue) >= 17, VBA.Len(sValue) >= 16)
|
|
If bIsLarge Then
|
|
Parser_Numbers = sValue
|
|
Else
|
|
Parser_Numbers = VBA.val(sValue)
|
|
End If
|
|
Exit Function
|
|
End If
|
|
Loop
|
|
End Function
|
|
|
|
Private Function Parser_Key() As String
|
|
Dim sChar$: sChar = Parser_ViewAny(1)
|
|
If sChar = """" Or sChar = "'" Then
|
|
Parser_Key = Parser_String
|
|
Else
|
|
Call Parser_RaiseError("Expected '""' or '''")
|
|
End If
|
|
End Function
|
|
|
|
' View nChars from input
|
|
Private Function Parser_ViewAny(Optional nChars& = 1) As String
|
|
If position_ + nChars - 1 > inputLen_ Then _
|
|
Call Parser_RaiseError("Unexpected end of input")
|
|
Parser_ViewAny = VBA.Mid$(json_, position_, nChars)
|
|
End Function
|
|
|
|
' View nChar symboms (skipping spaces)
|
|
Private Function Parser_ViewChar(Optional nChars& = 1) As String
|
|
Dim nPosition&: nPosition = position_
|
|
Do While nPosition > 0 And nPosition <= inputLen_ And VBA.Mid$(json_, nPosition, 1) = " "
|
|
nPosition = nPosition + 1
|
|
Loop
|
|
If nPosition + nChars - 1 > inputLen_ Then _
|
|
Call Parser_RaiseError("Unexpected end of input")
|
|
Parser_ViewChar = VBA.Mid$(json_, nPosition, nChars)
|
|
End Function
|
|
|
|
' Consumne nChars symbols from input
|
|
Private Function Parser_ConsumeAny(Optional nChars& = 1) As String
|
|
If position_ + nChars - 1 > inputLen_ Then _
|
|
Call Parser_RaiseError("Unexpected end of input")
|
|
Parser_ConsumeAny = VBA.Mid$(json_, position_, nChars)
|
|
Call Parser_Increment(nChars)
|
|
End Function
|
|
|
|
' Consume nChars from input (skipping spaces)
|
|
Private Function Parser_ConsumeChar(Optional nChars& = 1) As String
|
|
Call Parser_SkipWS
|
|
If position_ + nChars - 1 > inputLen_ Then _
|
|
Call Parser_RaiseError("Unexpected end of input")
|
|
Parser_ConsumeChar = VBA.Mid$(json_, position_, nChars)
|
|
Call Parser_Increment(nChars)
|
|
End Function
|
|
|
|
Private Function Parser_Increment(Optional nIncrement& = 1)
|
|
position_ = position_ + nIncrement
|
|
End Function
|
|
|
|
Private Function Parser_SkipWS()
|
|
Do While position_ <= inputLen_ And VBA.Mid$(json_, position_, 1) = " "
|
|
Call Parser_Increment
|
|
Loop
|
|
End Function
|
|
|
|
Private Function Parser_RaiseError(sMessage$)
|
|
Dim nStart&: nStart = position_ - 10
|
|
If nStart <= 0 Then _
|
|
nStart = 1
|
|
|
|
Dim nEnd&: nEnd = position_ + 10
|
|
If nEnd > inputLen_ Then _
|
|
nEnd = inputLen_
|
|
|
|
Dim sErrorMsg$: sErrorMsg = "Error parsing JSON:" & VBA.vbNewLine & _
|
|
VBA.Mid$(json_, nStart, nEnd - nStart + 1) & VBA.vbNewLine & _
|
|
VBA.Space$(position_ - nStart) & "^" & VBA.vbNewLine & _
|
|
sMessage
|
|
Call Err.Raise(ERR_PARSE_FAIL, "JSONConverter", sErrorMsg)
|
|
End Function
|
|
|
|
Private Function ArraySize(target As Variant, Optional nDimension% = 1) As Long
|
|
On Error GoTo INVALID_SIZE
|
|
|
|
ArraySize = UBound(target, nDimension) - LBound(target, nDimension) + 1
|
|
Exit Function
|
|
|
|
INVALID_SIZE:
|
|
ArraySize = -1
|
|
End Function
|
|
|
|
Private Function IsUndefined(ByVal vValue As Variant) As Boolean
|
|
' Empty / Nothing -> undefined
|
|
Select Case VBA.VarType(vValue)
|
|
Case VBA.vbEmpty
|
|
IsUndefined = True
|
|
Case VBA.vbObject
|
|
Select Case VBA.TypeName(vValue)
|
|
Case "Empty", "Nothing"
|
|
IsUndefined = True
|
|
End Select
|
|
End Select
|
|
End Function
|
|
|
|
Private Function EncodeStr(ByVal sText As Variant) As String
|
|
Dim sBuffer$: sBuffer = vbNullString
|
|
Dim nBufferPosition&
|
|
Dim nBufferLen&
|
|
|
|
Dim sChar$
|
|
Dim nCharCode&
|
|
Dim nPosition&
|
|
For nPosition = 1 To VBA.Len(sText)
|
|
sChar = VBA.Mid$(sText, nPosition, 1)
|
|
nCharCode = VBA.AscW(sChar)
|
|
|
|
' Convert negative complement into char code, see: https://support.microsoft.com/en-us/kb/272138
|
|
If nCharCode < 0 Then _
|
|
nCharCode = nCharCode + 65536
|
|
|
|
Select Case nCharCode
|
|
Case 34: sChar = "\"""
|
|
Case 92: sChar = "\\"
|
|
Case 47: If escapeSlash_ Then sChar = "\/"
|
|
Case 8: sChar = "\b"
|
|
Case 12: sChar = "\f"
|
|
Case 10: sChar = "\n"
|
|
Case 13: sChar = "\r"
|
|
Case 9: sChar = "\t"
|
|
Case 0 To 31, 127 To 65535: If escapeUnicode_ Then sChar = "\u" & VBA.Right$("0000" & VBA.Hex$(nCharCode), 4)
|
|
End Select
|
|
|
|
Call BufferAppend(sBuffer, sChar, nBufferPosition, nBufferLen)
|
|
Next nPosition
|
|
|
|
EncodeStr = BufferToString(sBuffer, nBufferPosition)
|
|
End Function
|
|
|
|
Private Function IsLargeNumber(sJson As Variant) As Boolean
|
|
Dim nLength&: nLength = VBA.Len(sJson)
|
|
If nLength < 16 Or nLength >= 100 Then _
|
|
Exit Function
|
|
|
|
IsLargeNumber = True
|
|
Dim nIndex&
|
|
For nIndex = 1 To nLength Step 1
|
|
Select Case VBA.Asc(VBA.Mid$(sJson, nIndex, 1))
|
|
Case 46, 48 To 57, 69, 101 ' Look for .|0-9|E|e
|
|
' Continue through characters
|
|
Case Else
|
|
IsLargeNumber = False
|
|
Exit Function
|
|
End Select
|
|
Next nIndex
|
|
End Function
|
|
|
|
Private Function GetWS(nIdent&) As String
|
|
If Not multiline_ Then _
|
|
Exit Function
|
|
If wsCount_ <> -1 Then
|
|
GetWS = VBA.Space$(nIdent * wsCount_)
|
|
Else
|
|
GetWS = VBA.String$(nIdent, wsCharacter_)
|
|
End If
|
|
End Function
|
|
|
|
Private Function BufferAppend(ByRef sBuffer$, ByRef vAppendage As Variant, ByRef nBufferPosition&, ByRef nBufferLen&)
|
|
' Allocate a large empty string and then copy string at append position instead of allocating for each append
|
|
Dim nAppendLen&: nAppendLen = VBA.Len(vAppendage)
|
|
|
|
If nAppendLen + nBufferPosition > nBufferLen Then
|
|
Dim nAddedLength&: nAddedLength = IIf(nAppendLen > nBufferLen, nAppendLen, nBufferLen)
|
|
sBuffer = sBuffer & VBA.Space$(nAddedLength)
|
|
nBufferLen = nBufferLen + nAddedLength
|
|
End If
|
|
|
|
' Access and modify buffer using Mid$
|
|
Mid$(sBuffer, nBufferPosition + 1, nAppendLen) = CStr(vAppendage)
|
|
nBufferPosition = nBufferPosition + nAppendLen
|
|
End Function
|
|
|
|
Private Function BufferToString(ByRef sBuffer$, nBufferPosition&) As String
|
|
If nBufferPosition > 0 Then _
|
|
BufferToString = VBA.Left$(sBuffer, nBufferPosition)
|
|
End Function
|