Created
June 24, 2011 20:06
-
-
Save villelahdenvuo/1045566 to your computer and use it in GitHub Desktop.
CoolJSON
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////// 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! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/////////////////// 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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 |
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
Arrays inside arrays working as of revision d7adcee46277442bfa5c3181f5407da9fc527f19
See: http://www.tuhoojabotti.com/r/prsc/JSON_JEA.png