Advertisement
omegastripes

VBA_JSON_parse_via_RegEx

Oct 26th, 2015
315
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Option Explicit
  2.  
  3. Sub JsonTest()
  4.     Dim strJsonString As String
  5.     Dim varJson As Variant
  6.     Dim strState As String
  7.     Dim varItem As Variant
  8.    
  9.     ' parse JSON string to object
  10.    ' root element can be the object {} or the array []
  11.    strJsonString = "{""a"":[{}, 0, ""value"", [{""stuff"":""content""}]], b:null}"
  12.     ParseJson strJsonString, varJson, strState
  13.    
  14.     ' checking the structure step by step
  15.    Select Case False ' if any of the checks is False, the sequence is interrupted
  16.        Case IsObject(varJson) ' if root JSON element is object {},
  17.        Case varJson.Exists("a") ' having property a,
  18.        Case IsArray(varJson("a")) ' which is array,
  19.        Case UBound(varJson("a")) >= 3 ' having not less than 4 elements,
  20.        Case IsArray(varJson("a")(3)) ' where forth element is array,
  21.        Case UBound(varJson("a")(3)) = 0 ' having the only element,
  22.        Case IsObject(varJson("a")(3)(0)) ' which is object,
  23.        Case varJson("a")(3)(0).Exists("stuff") ' having property stuff,
  24.        Case Else
  25.             MsgBox "Check the structure step by step" & vbCrLf & varJson("a")(3)(0)("stuff") ' then show the value of the last one property.
  26.    End Select
  27.    
  28.     ' direct access to the property if sure of structure
  29.    MsgBox "Direct access to the property" & vbCrLf & varJson.Item("a")(3)(0).Item("stuff") ' content
  30.    
  31.     ' traversing each element in array
  32.    For Each varItem In varJson("a")
  33.         ' show the structure of the element
  34.        MsgBox "The structure of the element:" & vbCrLf & BeautifyJson(varItem)
  35.     Next
  36.    
  37.     ' show the full structure starting from root element
  38.    MsgBox "The full structure starting from root element:" & vbCrLf & BeautifyJson(varJson)
  39.    
  40. End Sub
  41.  
  42. Sub BeautifyTest()
  43.     ' put sourse JSON string to "desktop\source.json" file
  44.    ' processed JSON will be saved to "desktop\result.json" file
  45.    Dim strDesktop As String
  46.     Dim strJsonString As String
  47.     Dim varJson As Variant
  48.     Dim strState As String
  49.     Dim strResult As String
  50.     Dim lngIndent As Long
  51.    
  52.     strDesktop = CreateObject("WScript.Shell").SpecialFolders.Item("Desktop")
  53.     strJsonString = ReadTextFile(strDesktop & "\source.json", -2)
  54.     ParseJson strJsonString, varJson, strState
  55.     If strState <> "Error" Then
  56.         strResult = BeautifyJson(varJson)
  57.         WriteTextFile strResult, strDesktop & "\result.json", -1
  58.     End If
  59.     CreateObject("WScript.Shell").PopUp strState, 1, , 64
  60. End Sub
  61.  
  62. Sub ParseJson(ByVal strContent As String, varJson As Variant, strState As String)
  63.     ' strContent - source JSON string
  64.    ' varJson - created object or array to be returned as result
  65.    ' strState - Object|Array|Error depending on processing to be returned as state
  66.    Dim objTokens As Object
  67.     Dim lngTokenId As Long
  68.     Dim objRegEx As Object
  69.     Dim bMatched As Boolean
  70.    
  71.     Set objTokens = CreateObject("Scripting.Dictionary")
  72.     lngTokenId = 0
  73.     Set objRegEx = CreateObject("VBScript.RegExp")
  74.     With objRegEx
  75.         ' specification http://www.json.org/
  76.        .Global = True
  77.         .MultiLine = True
  78.         .IgnoreCase = True
  79.         .Pattern = """(?:\\""|[^""])*""(?=\s*(?:,|\:|\]|\}))"
  80.         Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "str"
  81.         .Pattern = "(?:[+-])?(?:\d+\.\d*|\.\d+|\d+)e(?:[+-])?\d+(?=\s*(?:,|\]|\}))"
  82.         Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "num"
  83.         .Pattern = "(?:[+-])?(?:\d+\.\d*|\.\d+|\d+)(?=\s*(?:,|\]|\}))"
  84.         Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "num"
  85.         .Pattern = "\b(?:true|false|null)(?=\s*(?:,|\]|\}))"
  86.         Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "cst"
  87.         .Pattern = "\b[A-Za-z_]\w*(?=\s*\:)" ' unspecified name without quotes
  88.        Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "nam"
  89.         .Pattern = "\s"
  90.         strContent = .Replace(strContent, "")
  91.         .MultiLine = False
  92.         Do
  93.             bMatched = False
  94.             .Pattern = "<\d+(?:str|nam)>\:<\d+(?:str|num|obj|arr|cst)>"
  95.             Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "prp"
  96.             .Pattern = "\{(?:<\d+prp>(?:,<\d+prp>)*)?\}"
  97.             Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "obj"
  98.             .Pattern = "\[(?:<\d+(?:str|num|obj|arr|cst)>(?:,<\d+(?:str|num|obj|arr|cst)>)*)?\]"
  99.             Tokenize objTokens, objRegEx, strContent, lngTokenId, bMatched, "arr"
  100.         Loop While bMatched
  101.         .Pattern = "^<\d+(?:obj|arr)>$" ' unspecified top level array
  102.        If Not (.test(strContent) And objTokens.Exists(strContent)) Then
  103.             varJson = Null
  104.             strState = "Error"
  105.         Else
  106.             Retrieve objTokens, objRegEx, strContent, varJson
  107.             strState = IIf(IsObject(varJson), "Object", "Array")
  108.         End If
  109.     End With
  110. End Sub
  111.  
  112. Sub Tokenize(objTokens, objRegEx, strContent, lngTokenId, bMatched, strType)
  113.     Dim strKey As String
  114.     Dim strRes As String
  115.     Dim lngCopyIndex As Long
  116.     Dim objMatch As Object
  117.    
  118.     strRes = ""
  119.     lngCopyIndex = 1
  120.     With objRegEx
  121.         For Each objMatch In .Execute(strContent)
  122.             strKey = "<" & lngTokenId & strType & ">"
  123.             bMatched = True
  124.             With objMatch
  125.                 objTokens(strKey) = .Value
  126.                 strRes = strRes & Mid(strContent, lngCopyIndex, .FirstIndex - lngCopyIndex + 1) & strKey
  127.                 lngCopyIndex = .FirstIndex + .Length + 1
  128.             End With
  129.             lngTokenId = lngTokenId + 1
  130.         Next
  131.         strContent = strRes & Mid(strContent, lngCopyIndex, Len(strContent) - lngCopyIndex + 1)
  132.     End With
  133. End Sub
  134.  
  135. Sub Retrieve(objTokens, objRegEx, strTokenKey, varTransfer)
  136.     Dim strContent As String
  137.     Dim strType As String
  138.     Dim objMatches As Object
  139.     Dim objMatch As Object
  140.     Dim strName As String
  141.     Dim varValue As Variant
  142.     Dim objArrayElts As Object
  143.    
  144.     strType = Left(Right(strTokenKey, 4), 3)
  145.     strContent = objTokens(strTokenKey)
  146.     With objRegEx
  147.         .Global = True
  148.         Select Case strType
  149.             Case "obj"
  150.                 .Pattern = "<\d+\w{3}>"
  151.                 Set objMatches = .Execute(strContent)
  152.                 Set varTransfer = CreateObject("Scripting.Dictionary")
  153.                 For Each objMatch In objMatches
  154.                     Retrieve objTokens, objRegEx, objMatch.Value, varTransfer
  155.                 Next
  156.             Case "prp"
  157.                 .Pattern = "<\d+\w{3}>"
  158.                 Set objMatches = .Execute(strContent)
  159.                
  160.                 Retrieve objTokens, objRegEx, objMatches(0).Value, strName
  161.                 Retrieve objTokens, objRegEx, objMatches(1).Value, varValue
  162.                 If IsObject(varValue) Then
  163.                     Set varTransfer(strName) = varValue
  164.                 Else
  165.                     varTransfer(strName) = varValue
  166.                 End If
  167.             Case "arr"
  168.                 .Pattern = "<\d+\w{3}>"
  169.                 Set objMatches = .Execute(strContent)
  170.                 Set objArrayElts = CreateObject("Scripting.Dictionary")
  171.                 For Each objMatch In objMatches
  172.                     Retrieve objTokens, objRegEx, objMatch.Value, varValue
  173.                     If IsObject(varValue) Then
  174.                         Set objArrayElts(objArrayElts.Count) = varValue
  175.                     Else
  176.                         objArrayElts(objArrayElts.Count) = varValue
  177.                     End If
  178.                     varTransfer = objArrayElts.Items
  179.                 Next
  180.             Case "nam"
  181.                 varTransfer = strContent
  182.             Case "str"
  183.                 varTransfer = Mid(strContent, 2, Len(strContent) - 2)
  184.                 varTransfer = Replace(varTransfer, "\""", """")
  185.                 varTransfer = Replace(varTransfer, "\\", "\")
  186.                 varTransfer = Replace(varTransfer, "\/", "/")
  187.                 varTransfer = Replace(varTransfer, "\b", Chr(8))
  188.                 varTransfer = Replace(varTransfer, "\f", Chr(12))
  189.                 varTransfer = Replace(varTransfer, "\n", vbLf)
  190.                 varTransfer = Replace(varTransfer, "\r", vbCr)
  191.                 varTransfer = Replace(varTransfer, "\t", vbTab)
  192.                 .Global = False
  193.                 .Pattern = "\\u[0-9a-fA-F]{4}"
  194.                 Do While .test(varTransfer)
  195.                     varTransfer = .Replace(varTransfer, ChrW(("&H" & Right(.Execute(varTransfer)(0).Value, 4)) * 1))
  196.                 Loop
  197.             Case "num"
  198.                 varTransfer = Evaluate(strContent)
  199.             Case "cst"
  200.                 Select Case LCase(strContent)
  201.                     Case "true"
  202.                         varTransfer = True
  203.                     Case "false"
  204.                         varTransfer = False
  205.                     Case "null"
  206.                         varTransfer = Null
  207.                 End Select
  208.         End Select
  209.     End With
  210. End Sub
  211.  
  212. Function BeautifyJson(varJson As Variant) As String
  213.     Dim strResult As String
  214.     Dim lngIndent As Long
  215.     BeautifyJson = ""
  216.     lngIndent = 0
  217.     BeautyTraverse BeautifyJson, lngIndent, varJson, vbTab, 1
  218. End Function
  219.  
  220. Sub BeautyTraverse(strResult As String, lngIndent As Long, varElement As Variant, strIndent As String, lngStep As Long)
  221.     Dim arrKeys() As Variant
  222.     Dim lngIndex As Long
  223.     Dim strTemp As String
  224.  
  225.     Select Case VarType(varElement)
  226.         Case vbObject
  227.             If varElement.Count = 0 Then
  228.                 strResult = strResult & "{}"
  229.             Else
  230.                 strResult = strResult & "{" & vbCrLf
  231.                 lngIndent = lngIndent + lngStep
  232.                 arrKeys = varElement.Keys
  233.                 For lngIndex = 0 To UBound(arrKeys)
  234.                     strResult = strResult & String(lngIndent, strIndent) & """" & arrKeys(lngIndex) & """" & ": "
  235.                     BeautyTraverse strResult, lngIndent, varElement(arrKeys(lngIndex)), strIndent, lngStep
  236.                     If Not (lngIndex = UBound(arrKeys)) Then strResult = strResult & ","
  237.                     strResult = strResult & vbCrLf
  238.                 Next
  239.                 lngIndent = lngIndent - lngStep
  240.                 strResult = strResult & String(lngIndent, strIndent) & "}"
  241.             End If
  242.         Case Is >= vbArray
  243.             If UBound(varElement) = -1 Then
  244.                 strResult = strResult & "[]"
  245.             Else
  246.                 strResult = strResult & "[" & vbCrLf
  247.                 lngIndent = lngIndent + lngStep
  248.                 For lngIndex = 0 To UBound(varElement)
  249.                     strResult = strResult & String(lngIndent, strIndent)
  250.                     BeautyTraverse strResult, lngIndent, varElement(lngIndex), strIndent, lngStep
  251.                     If Not (lngIndex = UBound(varElement)) Then strResult = strResult & ","
  252.                     strResult = strResult & vbCrLf
  253.                 Next
  254.                 lngIndent = lngIndent - lngStep
  255.                 strResult = strResult & String(lngIndent, strIndent) & "]"
  256.             End If
  257.         Case vbInteger, vbLong, vbSingle, vbDouble
  258.             strResult = strResult & varElement
  259.         Case vbNull
  260.             strResult = strResult & "Null"
  261.         Case vbBoolean
  262.             strResult = strResult & IIf(varElement, "True", "False")
  263.         Case Else
  264.             strTemp = Replace(varElement, "\""", """")
  265.             strTemp = Replace(strTemp, "\", "\\")
  266.             strTemp = Replace(strTemp, "/", "\/")
  267.             strTemp = Replace(strTemp, Chr(8), "\b")
  268.             strTemp = Replace(strTemp, Chr(12), "\f")
  269.             strTemp = Replace(strTemp, vbLf, "\n")
  270.             strTemp = Replace(strTemp, vbCr, "\r")
  271.             strTemp = Replace(strTemp, vbTab, "\t")
  272.             strResult = strResult & """" & strTemp & """"
  273.     End Select
  274.    
  275. End Sub
  276.  
  277. Function ReadTextFile(strPath As String, lngFormat As Long) As String
  278.     ' lngFormat -2 - System default, -1 - Unicode, 0 - ASCII
  279.    With CreateObject("Scripting.FileSystemObject").OpenTextFile(strPath, 1, False, lngFormat)
  280.         ReadTextFile = ""
  281.         If Not .AtEndOfStream Then ReadTextFile = .ReadAll
  282.         .Close
  283.     End With
  284. End Function
  285.  
  286. Sub WriteTextFile(strContent As String, strPath As String, lngFormat As Long)
  287.     With CreateObject("Scripting.FileSystemObject").OpenTextFile(strPath, 2, True, lngFormat)
  288.         .Write (strContent)
  289.         .Close
  290.     End With
  291. End Sub
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement