LoadJSONC()
Posted: Fri Feb 23, 2024 10:29 pm
To make it possible to load JSON files with comments inside.
It is not an official JSON extension, but some companies does this.
To make it fast and without additional memory usage, I used the memory of the string and replace all comments with spaces.
Spaces are allowed inside of a JSON, so I can afterwards using ParseJSON() as usual.
I'm using a state machine to detect the different possibilities.
An example JSON:
It is not an official JSON extension, but some companies does this.
To make it fast and without additional memory usage, I used the memory of the string and replace all comments with spaces.
Spaces are allowed inside of a JSON, so I can afterwards using ParseJSON() as usual.
I'm using a state machine to detect the different possibilities.
Code: Select all
;
; LoadJSONC()
;
; https://www.purebasic.fr/english/viewtopic.php?t=83647
;
; V1.0.1 2024.02.24 allow single /
; V1.0.0 2024.02.23 first version
;
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
Enumeration LoadJSONC_States
#LoadJSONC_State_Search
#LoadJSONC_State_FirstSlashFound
#LoadJSONC_State_SecondSlashFound
#LoadJSONC_State_FirstAsteriskFound
#LoadJSONC_State_SecondAsteriskFound
EndEnumeration
Procedure.i LoadJSONC(JSON.i, Filename$, Flags.i=0)
Protected File.i, Format.i, JSON$, *CharPtr.Character, State.i, EscapeFlag.i
File = ReadFile(#PB_Any, Filename$)
If File
Format = ReadStringFormat(File)
JSON$ = ReadString(File, Format|#PB_File_IgnoreEOL)
CloseFile(File)
If JSON$ <> ""
*CharPtr = @JSON$
While *CharPtr\c <> #Null
Select State
Case #LoadJSONC_State_Search
Select *CharPtr\c
Case '/'
If Not EscapeFlag
; maybe start o a comment
State = #LoadJSONC_State_FirstSlashFound
EndIf
Case '\'
; the next character is escaped -> ignore it
EscapeFlag = #True
Default
EscapeFlag = #False
EndSelect
Case #LoadJSONC_State_FirstSlashFound
Select *CharPtr\c
Case '/'
; // detected -> replace them
*CharPtr - SizeOf(Character)
*CharPtr\c = ' '
*CharPtr + SizeOf(Character)
*CharPtr\c = ' '
State = #LoadJSONC_State_SecondSlashFound
Case '*'
; /* detected -> replace them
*CharPtr - SizeOf(Character)
*CharPtr\c = ' '
*CharPtr + SizeOf(Character)
*CharPtr\c = ' '
State = #LoadJSONC_State_FirstAsteriskFound
Default
State = #LoadJSONC_State_Search
EndSelect
Case #LoadJSONC_State_SecondSlashFound
; wait until end of line is reached
If *CharPtr\c = #LF Or *CharPtr\c = #CR
State = #LoadJSONC_State_Search
Else
*CharPtr\c = ' '
EndIf
Case #LoadJSONC_State_FirstAsteriskFound
; wait for second * -> replace everything
If *CharPtr\c = '*'
State = #LoadJSONC_State_SecondAsteriskFound
EndIf
*CharPtr\c = ' '
Case #LoadJSONC_State_SecondAsteriskFound
If *CharPtr\c = '/'
; */ found -> replace it and start searching for comments
*CharPtr\c = ' '
State = #LoadJSONC_State_Search
Else
; not a comment termination -> replace it ands wait for */
*CharPtr\c = ' '
State = #LoadJSONC_State_FirstAsteriskFound
EndIf
EndSelect
*CharPtr + SizeOf(Character)
Wend
JSON = ParseJSON(JSON, JSON$, Flags)
EndIf
EndIf
ProcedureReturn JSON
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
Define Filename$, JSON.i
Filename$ = OpenFileRequester("Choose a JSONC file", "", "JSONC|*.json;*jsonc", 0)
If Filename$
JSON = LoadJSONC(#PB_Any, Filename$)
If JSON
Debug ComposeJSON(JSON, #PB_JSON_PrettyPrint)
FreeJSON(JSON)
EndIf
EndIf
CompilerEndIf
Code: Select all
/*
* Description
*/
{
// rainbows
"uni/corn": /* ? */ "c\/*/ake"
}