Skip to content

Instantly share code, notes, and snippets.

@villelahdenvuo
Created June 24, 2011 20:06
Show Gist options
  • Save villelahdenvuo/1045566 to your computer and use it in GitHub Desktop.
Save villelahdenvuo/1045566 to your computer and use it in GitHub Desktop.
CoolJSON
///////////// Changelog \\\\\\\\\\\\\
Legend: * = Info-ed (you)
+ = Added
- = Removed
! = Fixed
~ = Changed
# = Todo-ed
--==" 1.0 "==--
* First real release (TM)
--==" 1.1 "==--
~ Renamed getJSON -> getJSONHandle
+ Support for object["lol"] syntax in getJSONHandle
(Also object[lol] works, but you should prefer
readability and use 'lol[""]' instead of 'lol.' :D)
+ getJSONPath
# Fix problem with empty key in getJSONHandle (Atm if you try to get object containing an empty key will return the key's value instead!)
--==" 1.2 "==--
! Empty keys in getJSONHandle
~ getJSONHandle is now stricter with the queries, you have to use [] for arrays and [""] for keys. ([] works for empty keys too, but is depreciated)
+/! Now keys can also contain escaped quotes (\")
--==" 1.3 "==--
! setJSON strings getting double quotes
! parseJSONFragment leaving empty 4 byte memoryblocks if parsing empty arrays/objects
! delJSON returning false on success...
+ nextJSON and prevJSON get next/prev object/array child.
+ insertJSON - inserts JSON to a container.
+ cutJSON, copyJSON, mergeJSON ...some more content manipulation functions.
+ quoteJSON helper, so you don't have to keep typing JSON_QUOT+"lol"+JSON_QUOT all the time!
/////////////////// CoolJSON \\\\\\\\\\\\\\\\\\\
// Contributors (Copyright 2011): \\
// * Ville 'tuhoojabotti' Lahdenvuo (Main dev) //
// - http://www.tuhoojabotti.com //
// * Atomimalli (Planning and insight) //
//////////////////////////////////////////////
// JSON PIG: (by jgs) _ //
// _ _ __....._ _ '-)-' //
// |_\_/ | .' '. '-)-' //
// / \/ \-'` //
// _| 6 6 ` | //
// /..\ | //
// \__/_, | / //
// '--.___ \ \ \ //
// / / /`----`;-. > //
// / / / / / / //
// /_/__/ /_/__/ //
// http://www.ascii-art.com/ /////
//////////////////////////////// LICENSE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Const VERSION = 1.3
// Containers
Const JSON_OBJ = 1
Const JSON_ARR = 2
// Datatypes
Const JSON_STR = 3
Const JSON_INT = 4
Const JSON_BOOL = 5
Const JSON_NULL = 6
Const JSON_FLOAT = 7
// Global(s)
Global JSON_QUOT As String, JSON_BSLASH As String, JSON_ITEMS As Integer
JSON_QUOT = Chr(34)
JSON_BSLASH = Chr(92)
JSON_ITEMS = 0
// Data stash!
Type JSON
Field p As Integer // Parent's type pointer
Field i As Integer // Type of the data (JSON_OBJ/ARR/STR/etc.)
Field m As Integer // Memblock ID for pointers to child data JSON_(OBJ|ARR) Or a JSON_BOOL's value
Field k As String // What key's value is this?
// Data fields
Field s As String // A String full of delicious data
Field f As Float // A fluffy float
EndType
//*****************************************//
//********************* LIBRARY METHODS ***//
//*****************************************//
//parseJSON:
' s - a JSON string or file
' returns a JSON type handle
Function parseJSON(s As String)
// It's a file so let's read it to a string
If FileExists(s) Then
f = OpenToRead(s)
s = ""
While Not EOF(f)
s = s + ReadLine(f)
Wend
EndIf
If Len(s) > 0 Then Return parseJSONFragment(s) // Well that was easy, huh? :D
EndFunction
//getJSONHandle:
' handle - JSON object to search the data from
' q - JS-like query for data example: "repository.users[3].nick"
' returns JSON type handle for the object queried
Function getJSONHandle(handle As Integer, q As String = "")
If handle = 0 Then Return False
If q = "" Then Return handle // Nothing to search :D
key$ = ""
// Validate the query
If InStr(q, "..") Or Left(q, 1) = "." Or Right(q, 1) = "." Then MakeError "getJSONHandle: Invalid query: " + q
d.JSON = ConvertToType(handle)
'Print "Handle(" + JSONType2Str(d\i) + "): " + JSON_QUOT + d\s + JSON_QUOT + " searching: " + q
q_first$ = GetWord(q, 1, ".")
If d\i = JSON_OBJ Then
// Nothing to search for, return this object.
If q_first = "" Then Return ConvertToInteger(d)
If d\m = 0 Then Return False
If Left(q_first, 2) = "[" + JSON_QUOT Then
key = parseJSONString(Mid(q_first, 2), "]") // object["key"] style
key = Mid(key, 2, Len(key) - 2) // Get rid of the quotes
// Rest to search
q = Mid(q, Len(key) + 5)
Else
key = GetWord(q_first, 1, "[") // object.key style
// Rest to search
q = Mid(q, Len(key) + 1)
If Left(q, 1) = "." Then q = Right(q, Len(q) - 1) // Remove possible leading dot.
EndIf
key = Replace(key, JSON_BSLASH + JSON_QUOT, JSON_QUOT) // \" -> "
n = Int(MEMBlockSize(d\m)/4)
While i < n // We must find the right child
child.JSON = ConvertToType(PeekInt(d\m, i * 4))
If child\k = key Then Return getJSONHandle(ConvertToInteger(child), q)
i = i + 1
Wend
Return False
ElseIf d\i = JSON_ARR Then
// If no index is given, return this array.
If q_first = "" Then Return ConvertToInteger(d)
If d\m = 0 Then Return False
If Left(q_first, 1) = "[" Then
index$ = Mid(q_first, 2, InStr(q_first, "]") - 2)
q = Mid(q, Len(index) + 3)
Else // The search should have an index but doesn't.
MakeError "getJSONHandle: Invalid array index: " + q_first
Return False
EndIf
If Left(q, 1) = "." Then q = Right(q, Len(q) - 1) // Remove possible leading dot.
// Let's make sure the index is a number
If index <> "0" And Int(index) = 0 Then MakeError "getJSONHandle: Invalid array index: " + q_first
n = Int(MEMBlockSize(d\m)/4)
i = Int(index)
If i < n And i >= 0 Then Return getJSONHandle(PeekInt(d\m, i * 4), q)
Return False
Else // It's finally some data, let's return it!
Return handle
EndIf
EndFunction
//nextJSON:
' handle - whose sister to get
' loop - wether to loop back from end to start [OPTIONAL] Defaults to false
' returns the next handle from the current one or false on failure
Function nextJSON(handle As Integer, loop As Integer = 0)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p <> 0 Then
d2.JSON = ConvertToType(d\p)
If d2\m > 1 Then
n = Int(MEMBlockSize(d2\m)/4) - 1
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then // Found it!
If i <> n Then // Good, grab the next one
Return PeekInt(d2\m, (i + 1) * 4)
ElseIf loop Then // Loop back to start
Return PeekInt(d2\m, 0)
EndIf
EndIf
Next i
EndIf
EndIf
EndFunction
//prevJSON:
' handle - whose sister to get
' loop - wether to loop back from start to end [OPTIONAL] Defaults to false
' returns the previous handle from the current one or false on failure
Function prevJSON(handle As Integer, loop As Integer = 0)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p <> 0 Then
d2.JSON = ConvertToType(d\p)
If d2\m > 1 Then
n = Int(MEMBlockSize(d2\m)/4) - 1
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then // Found it!
If i > 0 Then // Good, grab the next one
Return PeekInt(d2\m, (i - 1) * 4)
ElseIf loop Then // Loop back to start
Return PeekInt(d2\m, n * 4)
EndIf
EndIf
Next i
EndIf
EndIf
EndFunction
//getJSONPath:
' handle - JSON object which path to get
' parent - Handle, where to stop [OPTIONAL] Default is to find root object/array
Function getJSONPath$(handle As Integer, parent As Integer = 0)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p <> 0 Then
d2.JSON = ConvertToType(d\p)
Select d2\i
Case 1 // OBJECT so we have a key
If d\p = parent Then
Return d\k
Else
par$ = getJSONPath(d\p, parent)
ret$ = d\k
If par <> "" Then
If ret = "" Then Return par + "[" + JSON_QUOT + ret + JSON_QUOT + "]"
Return par + "." + ret
Else
If ret = "" Then Return "[" + JSON_QUOT + ret + JSON_QUOT + "]"
Return ret
EndIf
EndIf
Case 2 // ARRAY so we must return the index
n = Int(MEMBlockSize(d2\m)/4) - 1
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then
If d\p = parent Then
Return "[" + i + "]"
Else
Return getJSONPath(d\p, parent) + "[" + i + "]"
EndIf
EndIf
Next i
Default
Return ""
EndSelect
Else
Return ""
EndIf
EndFunction
//setJSON:
' handle - JSON-object to set the data to
' value - a string of the data to be set
' key - if you wish to set the key [OPTIONAL]
' returns true on success and false on failure
Function setJSON(handle As Integer, value As String, key As String = "")
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
// Update the key
If key <> "" Then
// But only if the parent is an object
d2.JSON = ConvertToType(d\p)
If d2\i = JSON_OBJ Then
d\k = key
Return True
EndIf
Return False
EndIf
// Update the value
t$ = Left(value, 1)
If t = JSON_QUOT Then ' STRING
d\i = JSON_STR
d\s = Replace(Mid(value, 2, Len(value) - 2), JSON_BSLASH + JSON_QUOT, JSON_QUOT)
ElseIf t = "t" Then ' TRUE
d\i = JSON_BOOL
d\m = True
ElseIf t = "f" Then ' FALSE
d\i = JSON_BOOL
d\m = False
ElseIf t = "n" Then ' NULL
d\i = JSON_NULL
ElseIf t = "-" Or t = "0" Or ( t <> "0" And Int(t) <> 0)
If InStr(value, ".") Then ' FLOAT
d\i = JSON_FLOAT
d\f = Float(value)
Else ' INTEGER
d\i = JSON_INT
d\f = Int(value)
EndIf
Else
Return False
EndIf
Return True
EndFunction
//insertJSON:
' parent - the object or array whitch to insert new content
' handle - a handle to the content to be inserted. It has to be be parentless, we do not want orphans.
' key - key on objects or index on arrays (use end or e to add to the end of the array)
' returns false on failure, otherwise true
Function insertJSON(parent As Integer, handle As Integer, key As String = "")
If parent And handle Then
d.JSON = ConvertToType(handle)
d2.JSON = ConvertToType(parent)
If d2\m Then
s = MEMBlockSize(d2\m)
Else
Return False
EndIf
Select d2\i
Case 1 // OBJ
d\p = parent
d\k = key
ResizeMEMBlock d2\m, s + 4
PokeInt d2\m, s, handle
Return True
Case 2 // ARR
If Lower(key) = "e"+"nd" Or Lower(key) = "e" Then
i = s
Else
i = Int(key) * 4
EndIf
d\p = parent
ResizeMEMBlock d2\m, s + 4
If s - i Then MemCopy d2\m, i, d2\m, i + 4, s - i
PokeInt d2\m, i, handle
Return True
EndSelect
EndIf
EndFunction
//mergeJSON:
' parent - Object to merge to
' handle - What to merge
' index - Where to merge (arrays) (use end or e to add to the end of the array)
' returns true on success and false on failure
Function mergeJSON(parent As Integer, handle As Integer, index As String = "end")
If parent And handle Then
d.JSON = ConvertToType(handle)
d2.JSON = ConvertToType(parent)
If d2\m Then
s = MEMBlockSize(d2\m)
Else
Return False
EndIf
Select d2\i
Case 1 'OBJ
If d\i = JSON_OBJ Then
l=MEMBlockSize(d\m)
For j = 0 To RoundDown(l / 4) - 1 // Change the parents
d3.JSON = ConvertToType(PeekInt(d\m, j * 4))
d3\p = parent
Next j
ResizeMEMBlock d2\m, s + l
If l Then MemCopy d\m, 0, d2\m, s, l
Return True
Else
Return False
EndIf
Case 2 'ARR
If Lower(index) = "end" Or Lower(index) = "e"
i = s
Else
i = Int(index) * 4
EndIf
If d\i = JSON_ARR Then // Type has to be array to merge
l = MEMBlockSize(d\m)
For j = 0 To RoundDown(l / 4) - 1 // Change the parents
d3.JSON = ConvertToType(PeekInt(d\m, j * 4))
d3\p = parent
Next j
ResizeMEMBlock d2\m, s + l
If s - i Then MemCopy d2\m, i, d2\m, s + i, s - i
MemCopy d\m, 0, d2\m, i, l
Delete d
Return True
Else
Return False
EndIf
EndSelect
EndIf
EndFunction
//cutJSON:
' handle - handle of JSON object to move
' newparent - handle to to the new location
' returns false or true depending on the success of the operation
Function cutJSON(handle, newparent, key As String = "")
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p Then
d2.JSON = ConvertToType(d\p)
If d2\m Then
n = Int(MEMBlockSize(d2\m)/4) - 1
If n = 0 Then // Memblock only has one item...
DeleteMEMBlock d2\m
d2\m = 0
Return insertJSON(newparent, handle, key)
EndIf
// Find the missing child!
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then // Found it!
If i = n Then // It's the last, so just resize the memblock
ResizeMEMBlock d2\m, n * 4
Exit
Else // Move the memory over it and resize the memblock
MemCopy d2\m, (i + 1) * 4, d2\m, 0, (n - i) * 4
ResizeMEMBlock d2\m, n * 4
Exit
EndIf
EndIf
Next i
EndIf
EndIf
Return insertJSON(newparent, handle, key)
EndFunction
//moveJSON:
' handle - handle of JSON object to move
' newparent - handle to to the new location
' returns success
Function moveJSON(handle, newparent, key As String = "")
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p Then
d2.JSON = ConvertToType(d\p)
If d2\m Then
n = Int(MEMBlockSize(d2\m)/4) - 1
If n = 0 Then // Memblock only has one item...
DeleteMEMBlock d2\m
d2\m = 0
Delete d
Return True
EndIf
// Find the missing child!
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then // Found it!
If i = n Then // It's the last, so just resize the memblock
ResizeMEMBlock d2\m, n * 4
Exit
Else // Move the memory over it and resize the memblock
MemCopy d2\m, (i + 1) * 4, d2\m, 0, (n - i) * 4
ResizeMEMBlock d2\m, n * 4
Exit
EndIf
EndIf
Next i
EndIf
EndIf
Return insertJSON(newparent, handle, key)
EndFunction
//delJSON:
' handle - handle to JSON object to destroy!
' parent - Delete from parent too [INTERNAL]
' returns true on success and false on failure
Function delJSON(handle As Integer, parent As Integer = 1)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\p And parent Then
d2.JSON = ConvertToType(d\p)
If (d2\i = JSON_OBJ Or d2\i = JSON_ARR) And d2\m <> 0 Then
n = RoundDown(MEMBlockSize(d2\m)/4) - 1
If n = 0 Then // Memblock only has one item...
DeleteMEMBlock d2\m
d2\m = 0
Delete d
Return True
EndIf
'For i = 0 To n
' Print PeekInt(d2\m, i * 4)
'Next i
'Print "___"
// Find the handle!
For i = 0 To n
If PeekInt(d2\m, i * 4) = handle Then // Found it!
Print handle
If i = n Then // It's the last, so just resize the memblock
ResizeMEMBlock d2\m, n * 4
Exit
Else // Move the memory over it and resize the memblock
MemCopy d2\m, (i + 1) * 4, d2\m, i * 4, (n - i) * 4
ResizeMEMBlock d2\m, n * 4
Exit
EndIf
EndIf
Next i
'Print "___"
'n = RoundDown(MEMBlockSize(d2\m)/4) - 1
'For i = 0 To n
' Print PeekInt(d2\m, i * 4)
'Next i
EndIf
EndIf
// Remember to kill the children too!
If (d\i = JSON_OBJ Or d\i = JSON_ARR) And d\m <> 0 Then
n = Int(MEMBlockSize(d\m)/4) - 1
For i = 0 To n
delJSON(PeekInt(d\m, i * 4), False)
Next i
DeleteMEMBlock d\m
EndIf
Delete d
Return True
EndFunction
//readJSONStr:
' handle - JSON type handle
' returns the value of the <handle> in a string
Function readJSONStr$(handle As Integer)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\i = JSON_STR Then
Return d\s
ElseIf d\i = JSON_NULL Then
Return "n"+"ull"
ElseIf d\i = JSON_INT Then
Return Int(d\f)
ElseIf d\i = JSON_FLOAT Then
Return Float(d\f)
ElseIf d\i = JSON_BOOL Then
Return d\m
Else
Return ""
EndIf
EndFunction
//readJSONInt:
' handle - JSON_INT or JSON_BOOL handle
' returns the value of the <handle> as an integer
Function readJSONInt%(handle As Integer)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\i = JSON_INT Then
Return Int(d\f)
ElseIf d\i = JSON_BOOL Then
Return d\m
EndIf
EndFunction
//readJSONFloat:
' handle - JSON_FLOAT handle
' returns the value of the <handle> as a float
Function readJSONFloat#(handle As Integer)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\i = JSON_FLOAT Then
Return Float(d\f)
EndIf
EndFunction
//saveJSON:
' f - filepath, created if not existing
' handle - JSON_OBJ or JSON_ARR to save to <f>ile
' returns false on failure, true on success
Function saveJSON(f As String, handle As Integer)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\i <> JSON_OBJ And d\i <> JSON_ARR Then Return False
If FileExists(f) Then DeleteFile f
fh = OpenToWrite(f)
WriteLine fh, renderJSON(handle)
CloseFile fh
Return True
EndFunction
//clearJSON - Clears the whole JSON type
Function clearJSON()
For d.JSON = Each JSON
If d\m > 1 Then DeleteMEMBlock d\m
If d <> NULL Then Delete d
Next d
JSON_ITEMS = 0
EndFunction
//JSONLen:
' handle - Object or Array whose length to return
Function JSONLen(handle As Integer)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
If d\i = JSON_OBJ Or d\i = JSON_ARR Then
If d\m <> 0 Then Return RoundDown(MEMBlockSize(d\m) / 4)
EndIf
EndFunction
//quoteJSON:
' s - string to quote
' char - what characters to replace with quotes [OPTIONAL] Defaults to '
Function quoteJSON(s As String, char As String = "'")
Return Replace(s, char, JSON_QUOT)
EndFunction
//*****************************************//
//********************* FOR INTERNAL USE **//
//*****************************************//
remstart
Field p As Integer // Parent's type pointer
Field i As Integer // Type of the data (JSON_OBJ/ARR/STR/etc.)
Field m As Integer // Memblock ID for pointers to child data JSON_(OBJ|ARR) Or a JSON_BOOL's value
Field k As String // What key's value is this?
// Data fields
Field s As String // A String full of delicious data
Field f As Float // A fluffy float
remend
//dumpJSON:
' file - file to dump the raw JSON data to
Function dumpJSON(file As String)
f = OpenToEdit(file)
SeekFile f, FileSize(file)
WriteLine f, ""
WriteLine f, ""
WriteLine f, "JSON DEBUG DUMP:"
For d.JSON = Each JSON
WriteLine f, "__________________"
WriteLine f, LSet("Parent:", 10) + d\p
WriteLine f, LSet("This:", 10) + ConvertToInteger(d)
WriteLine f, LSet("Type:", 10) + JSONType2Str(d\i)
If d\m > 1 Then
WriteLine f, LSet("MemSize:", 10) + MEMBlockSize(d\m)
Else
If d\i = JSON_BOOL Then
WriteLine f, LSet("Bool:", 10) + d\m
Else
WriteLine f, LSet("MemBlock:", 10) + d\m
EndIf
EndIf
If d\k <> "" Then WriteLine f, LSet("Key:", 10) + d\k
If d\s <> "" Then WriteLine f, LSet("String:", 10) + d\s
if d\f <> 0.0 Then WriteLine f, LSet("Float:", 10) + d\f
Next d
CloseFile(f)
EndFunction
//JSONType2Str:
' i - type constant to convert to string
' returns a string representation of the <i>
Function JSONType2Str(i As Integer)
Select i
Case 1
Return "JSON_OBJ"
Case 2
Return "JSON_ARR"
Case 3
Return "JSON_STR"
Case 4
Return "JSON_INT"
Case 5
Return "JSON_BOOL"
Case 6
Return "JSON_NULL"
Case 7
Return "JSON_FLOAT"
Default
Return "UNDEFINED"
EndSelect
EndFunction
//parseJSONFragment - Used to recursively parse the JSON-object tree.
' s - JSON fragment string to parse, must be valid JSON obviously.
' returns handle to the type parsed from <s>
Function parseJSONFragment(s As String, key As String = "", parent As Integer = 0)
s = Trim(s)
JSON_ITEMS = JSON_ITEMS + 1
d.JSON = New(JSON)
d\k = key // If it's a keys value, let's save it
d\p = parent // Also let's save the parent type
t$ = Left(s, 1)
If t = "{" Then ' OBJECT
'Print "OBJ" + s
d\i = JSON_OBJ
t = Trim(Mid(s, 2, Len(s) - 2))
length = Len(t)
i = 1
d\m = MakeMEMBlock(4)
p = 0
count = 0
While i < length
ResizeMEMBlock d\m, p + 4
c$ = Mid(t, i)
key$ = Replace(parseJSONString(Trim(c), ":"), JSON_BSLASH + JSON_QUOT, JSON_QUOT)
value$ = parseJSONString(Trim(Mid(c, InStr(c, ":", Len(key)) + 1)))
PokeInt d\m, p, parseJSONFragment(value, Mid(key, 2, Len(key) - 2), ConvertToInteger(d))
count = count + 1
If Not InStr(c, ",", Len(key + value) + 1) Then Exit
i = i + InStr(c, ",", Len(key + value) + 1)
p = p + 4
Wend
If count = 0 Then DeleteMEMBlock d\m: d\m = 0
Return ConvertToInteger(d)
ElseIf t = "[" Then ' ARRAY
'Print "ARR: "+s
d\i = JSON_ARR
t = Trim(Mid(s, 2, Len(s) - 2))
length = Len(t)
i = 1
d\m = MakeMEMBlock(4)
p = 0
count = 0
While i < length
c$ = Mid(t, i)
ResizeMEMBlock d\m, p + 4
item$ = parseJSONString(Trim(c))
PokeInt d\m, p, parseJSONFragment(item, "", ConvertToInteger(d))
count = count + 1
If Not InStr(c, ",", Len(item)) Then Exit
i = i + InStr(c, ",", Len(item))
p = p + 4
Wend
If count = 0 Then DeleteMEMBlock d\m: d\m = 0
Return ConvertToInteger(d)
ElseIf t = JSON_QUOT Then ' STRING
'Print "S"+"TR: "+s
d\i = JSON_STR
d\s = Replace(Mid(s, 2, Len(s) - 2), JSON_BSLASH + JSON_QUOT, JSON_QUOT)
ElseIf t = "t" Then ' TRUE
'Print "T"+"RUE"
d\i = JSON_BOOL
d\m = True
ElseIf t = "f" Then ' FALSE
'Print "F"+"ALSE"
d\i = JSON_BOOL
d\m = False
ElseIf t = "n" Then ' NULL
'Print "NULL"
d\i = JSON_NULL
ElseIf t = "-" Or t = "0" Or ( t <> "0" And Int(t) <> 0)
If InStr(s, ".") Then ' FLOAT
'Print "F"+"LOAT"+s
d\i = JSON_FLOAT
d\f = Float(s)
Else ' INTEGER
'Print "I"+"NT: "+s
d\i = JSON_INT
d\f = Int(s)
EndIf
Else ' ILLEGAL DATA >:|
MakeError "Malformed JSON String:" + Chr(13)+Chr(10)+Chr(13)+Chr(10) + s
EndIf
Return ConvertToInteger(d)
EndFunction
//parseJSONString - Parses string considering objects, arrays and strings
' s - String to parse
' e - Letter to end to [OPTIONAL]
' returns <s> that is cropped to first <e> or EOS (End Of String)
Function parseJSONString$(s As String, e As String = ",")
t$ = ""
i = 1
n = CountWords(s, e) + 1
si = 0 // Number of { and [ minus number of ] and }
ss = False // Are we inside a string or not.
ii = 1
quots = 0
rs$ = ""
While i < n
t = GetWord(s, i, e)
rs = rs + t
// Count the number of " then minus the number of \" then we have the amount of string start and ends
quots = quots + (Int(CountWords(t, JSON_QUOT)) - 1) - (Int(CountWords(t, JSON_BSLASH + JSON_QUOT)) - 1)
// Now if we have a paired number of ", it's good to go because the string has ended, so the <e> we're looking at is not inside a string.
If Not(quots Mod 2) Then
If e = ":" Then Return rs // For keys it's this simple, becuase they are just strings
If CountWords(rs, "[") = 1 And CountWords(rs, "{") = 1 Then Return rs //Oh nice, no arrays or objects to mess with
// Well this is the slow part, tokenizing the string finding arrays, objects and strings
'Print rs
While ii < Len(rs) + 1
tt$ = Mid(rs, ii, 1) // Get token
If InStr("[{", tt) And ss = False Then si = si + 1 // an array or object starting
If InStr("}]", tt) And ss = False Then si = si - 1 // an array or object ending
If tt = JSON_QUOT Then // String starting or ending 890ms
If ii = 1 Then
ss = Not ss
Else // Consider \"
If Mid(t, ii - 1, 1) <> JSON_BSLASH Then ss = Not ss
EndIf
EndIf
ii = ii + 1
Wend
If ss = False And si = 0 Then Return rs // Not in a string and no open objects or arrays :)
EndIf
rs = rs + e
i = i + 1
Wend
Return s
EndFunction
//renderJSON:
' handle - JSON handle to render to string
Function renderJSON$(handle As Integer, i As Integer = 0)
If handle = 0 Then Return False
d.JSON = ConvertToType(handle)
lc$ = Chr(13) + Chr(10)
s$ = ""
ind$ = String(" ", i)
If d\k <> "" Then // key
key$ = JSON_QUOT + Replace(d\k, JSON_QUOT, JSON_BSLASH + JSON_QUOT) + JSON_QUOT
Else // But the key can also be empty...
If d\p <> 0 Then
d2.JSON = ConvertToType(d\p)
If d2\i = JSON_OBJ Then key$ = JSON_QUOT + JSON_QUOT
EndIf
EndIf
ind2$ = ind
If key Then
s = s + ind + key + ": "
ind = ""
EndIf
Select d\i
Case 1
If d\m <> 0 Then
s = s + ind + "{" + lc
i = i + 1
n = Int(MEMBlockSize(d\m)/4) - 1
For ii = 0 To n
s = s + renderJSON(PeekInt(d\m, ii * 4), i)
If ii <> n Then s = s + ","
s = s + lc
Next ii
s = s + ind2 + "}"
Else
s = s + ind + "{}"
EndIf
Case 2
If d\m <> 0 Then
s = s + ind + "[" + lc
i = i + 1
n = Int(MEMBlockSize(d\m)/4) - 1
For ii = 0 To n
s = s + renderJSON(PeekInt(d\m, ii * 4), i)
If ii <> n Then s = s + ","
s = s + lc
Next ii
s = s + ind2 + "]"
Else
s = s + ind + "[]"
EndIf
Case 3
s = s + ind + JSON_QUOT + replace(d\s, JSON_QUOT, JSON_BSLASH + JSON_QUOT) + JSON_QUOT
Case 4
s = s + ind + Int(d\f)
Case 5
If d\m Then
s = s + ind + "t"+"rue"
Else
s = s + ind + "f"+"alse"
EndIf
Case 6
s = s + ind + "n"+"ull"
Case 7
s = s + ind + d\f
Default
Return False
EndSelect
Return s
EndFunction
//*****************************************//
//********************* TESTING SUITE *****//
//*****************************************//
SCREEN 500,600
file$= "" // Test file in a string
file = file + "{"
file = file + " "+JSON_QUOT+"tes:t"+JSON_QUOT+": ["
file = file + " "+JSON_QUOT+"lol \"+JSON_QUOT+"lul"+JSON_QUOT+","
file = file + " true,"
file = file + " ["
file = file + " 3.6,"
file = file + " {"
file = file + " "+JSON_QUOT+"arr"+JSON_QUOT+": 2,"
file = file + " "+JSON_QUOT+JSON_QUOT+": 69"
file = file + " }"
file = file + " ]"
file = file + " ],"
file = file + " "+JSON_QUOT+"lo\"+JSON_QUOT+"l"+JSON_QUOT+": {"
file = file + " "+JSON_QUOT+"lul"+JSON_QUOT+": 1.337"
file = file + " },"
file = file + " "+JSON_QUOT+"key"+JSON_QUOT+": "+JSON_QUOT+"{haha!}"+JSON_QUOT+","
file = file + " "+JSON_QUOT+JSON_QUOT+": "+JSON_QUOT+"empty key's value"+JSON_QUOT+""
file = file + "}"
CenterText 250, 0, "//////////// STARTING TESTING PROTOCOLS \\\\\\\\\\\\"
CenterText 250, 0, "__________________________"
Print ""
Print ""
Print RSet("_______", 55)
Print Replace(RSet("|PARSING", 55)," ","_")+"|_______"
ti = Timer()
test = parseJSON(file)
'test = parseJSON("test.json")
'test = parseJSON("test2.json") // You can load files too.
tim = Timer() - ti
Print "Took: " + tim + "ms"
For js.JSON = Each JSON
nu = nu + 1
Next js
Print "Items: " + nu
Print RSet("_______", 55)
Print Replace(RSet("|READING", 55)," ","_")+"|_______"
ti = Timer()
q_test$ = "tes:t[2][1].arr|tes:t[2][1]["+JSON_QUOT+JSON_QUOT+"]|tes:t[0]|lo\"+JSON_QUOT+"l["+JSON_QUOT+"lul"+JSON_QUOT+"]|key|["+JSON_QUOT+"lol"+JSON_QUOT+"][0].lul" // Queries separated with |
For i = 1 To CountWords(q_test, "|")
h = getJSONHandle(test, GetWord(q_test, i, "|"))
If h Then
Print LSet("("+h+") " + JSON_QUOT + GetWord(q_test, i, "|") + JSON_QUOT, 30) + " -> " + JSON_QUOT + readJSONStr(h) + JSON_QUOT
Else
Print LSet(JSON_QUOT + GetWord(q_test, i, "|") + JSON_QUOT, 30) + " -> " + "Not Found! "
EndIf
Next i
Print ""
Print "Setting value of lo"+JSON_QUOT+"l.lul to 1337"
setJSON(getJSONHandle(test, "lo\"+JSON_QUOT+"l.lul"), 1337)
Print ""
path$ = "tes:t[2][1][]"
parent$ = "tes:t[2]"
h = getJSONHandle(test, path)
Print "Path " + JSON_QUOT + path + JSON_QUOT + " (" + h + ") till " + JSON_QUOT + parent + JSON_QUOT + ": " + getJSONPath(h, getJSONHandle(test, parent))
Print "Path for ["+JSON_QUOT+JSON_QUOT+"]: " + getJSONPath(getJSONHandle(test, "[]"))
Print ""
Print "Deleting value of tes:t[2][0], tes:t[2][1] and " + JSON_QUOT + JSON_QUOT
delJSON(getJSONHandle(test, "tes:t[2][0]"))
delJSON(getJSONHandle(test, "tes:t[2][0]"))
delJSON(getJSONHandle(test, "[]"))
Print ""
Print "Inserting some sweets to JSON:"
If insertJSON(test, parseJSON("{" + JSON_QUOT + "kinuski" + JSON_QUOT + ": true}"),"sweets") Then Print "Kinuski Success!"
If insertJSON(getJSONHandle(test, "sweets"), parseJSON("["+JSON_QUOT+"Haribo"+JSON_QUOT+","+JSON_QUOT+"Fazer"+JSON_QUOT+","+JSON_QUOT+"Karvapallo"+JSON_QUOT+"]"), "brands") Then Print "Brand Success!"
If mergeJSON(getJSONHandle(test, "sweets.brands"), parseJSON("["+JSON_QUOT+"Marabou"+JSON_QUOT+","+JSON_QUOT+"Halva"+JSON_QUOT+","+JSON_QUOT+"haisuli"+JSON_QUOT+"]"), "2") Then Print "Merge Success!"
If insertJSON(getJSONHandle(test, "sweets.brands"), parseJSON("{"+JSON_QUOT+"Subclass"+JSON_QUOT+":"+JSON_QUOT+"Fazer Leipomot"+JSON_QUOT+"}"), "end") Then Print "Insert Success!"
If mergeJSON(test, parseJSON("{"+JSON_QUOT+"mergetest"+JSON_QUOT+":"+JSON_QUOT+"Random data. :)"+JSON_QUOT+"}")) Then Print "Object merging success"
tim = Timer() - ti
Print ""
Print "Took: " + tim + "ms"
Print "Ran: " + CountWords(q_test, "|") + " tests"
Print RSet("______", 55)
Print Replace(RSet("|SAVING", 55)," ","_")+"|_______"
f$ = "save.json"
Print "Saving " + JSON_QUOT + f + JSON_QUOT
ti = Timer()
saveJSON(f, test)
tim = Timer() - ti
Print "Dumping..."
dumpJSON(f)
Print "Took: " + tim + "ms"
Print "Filesize: " + FileSize(f) + " bytes"
Print RSet("_____", 55)
Print Replace(RSet("|DONE!", 55)," ","_")+"|_______"
Print ""
Print "Press Enter to open the " + f + " or the Anykey to exit..."
key = WaitKey()
If key = 28 Or key = 13 Then Execute("notepad.exe " + f)
{
"tes:t": [
"lol \"lul",
true,
[]
],
"lo\"l": {
"lul": 1337
},
"key": "{haha!}",
"sweets": {
"kinuski": true,
"brands": [
"Haribo",
"Fazer",
"Marabou",
"Halva",
"haisuli",
"Karvapallo",
{
"Subclass": "Fazer Leipomot"
}
]
},
"mergetesti": "Fazer Leipomot"
}
JSON DEBUG DUMP:
__________________
Parent: 0
This: 10870616
Type: JSON_OBJ
MemSize: 20
__________________
Parent: 10870616
This: 10872224
Type: JSON_ARR
MemSize: 12
Key: tes:t
__________________
Parent: 10872224
This: 11035600
Type: JSON_STR
MemBlock: 0
String: lol "lul
__________________
Parent: 10872224
This: 11035624
Type: JSON_BOOL
Bool: 1
__________________
Parent: 10872224
This: 11036080
Type: JSON_ARR
MemBlock: 0
__________________
Parent: 11037376
This: 11037800
Type: JSON_INT
MemBlock: 0
Key: arr
Float: 2.0
__________________
Parent: 11037376
This: 11038200
Type: JSON_INT
MemBlock: 0
Float: 69.0
__________________
Parent: 10870616
This: 10872560
Type: JSON_OBJ
MemSize: 4
Key: lo"l
__________________
Parent: 10872560
This: 11036312
Type: JSON_INT
MemBlock: 0
Key: lul
Float: 1337.0
__________________
Parent: 10870616
This: 11035936
Type: JSON_STR
MemBlock: 0
Key: key
String: {haha!}
__________________
Parent: 10870616
This: 10872392
Type: JSON_OBJ
MemSize: 8
Key: sweets
__________________
Parent: 10872392
This: 10872656
Type: JSON_BOOL
Bool: 1
Key: kinuski
__________________
Parent: 10872392
This: 10872440
Type: JSON_ARR
MemSize: 28
Key: brands
__________________
Parent: 10872440
This: 10872464
Type: JSON_STR
MemBlock: 0
String: Haribo
__________________
Parent: 10872440
This: 11037136
Type: JSON_STR
MemBlock: 0
String: Fazer
__________________
Parent: 10872440
This: 11036752
Type: JSON_STR
MemBlock: 0
String: Karvapallo
__________________
Parent: 10872440
This: 11037752
Type: JSON_STR
MemBlock: 0
String: Marabou
__________________
Parent: 10872440
This: 11038376
Type: JSON_STR
MemBlock: 0
String: Halva
__________________
Parent: 10872440
This: 11035672
Type: JSON_STR
MemBlock: 0
String: haisuli
__________________
Parent: 10872440
This: 11054240
Type: JSON_OBJ
MemSize: 4
__________________
Parent: 11054240
This: 11037776
Type: JSON_STR
MemBlock: 0
Key: Subclass
String: Fazer Leipomot
__________________
Parent: 0
This: 10870784
Type: JSON_OBJ
MemSize: 4
__________________
Parent: 10870616
This: 11036272
Type: JSON_STR
MemBlock: 0
Key: mergetesti
String: Fazer Leipomot
@villelahdenvuo
Copy link
Author

Arrays inside arrays working as of revision d7adcee46277442bfa5c3181f5407da9fc527f19

See: http://www.tuhoojabotti.com/r/prsc/JSON_JEA.png

@villelahdenvuo
Copy link
Author

Saving json is now possible, see testsave.json as an example. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment