What I wanted, was an easy implementation like in Javascript (this might be slightly simplified, but it's really easy to access JsON nodes in JS (obviously, json was made for javascript)):
Code: Select all
json = {foo:bar};
print(json.foo); //prints "bar"
Code: Select all
Define json.s = "{foo:bar}"
JSON::eval(json)
Debug JSON::Get("foo") ; will debug: bar
So here is some info and further down the code:
Version 0.1a (first release) 30.09.2014
- This module does NOT validate the json string. Perhaps there's an easy way to do it with PB (like the XML example demonstrates for example). So if your json string contains errors, you will get weird results.
- TODO: Currently, only one json object (string) is supported. Will add an ID-system later
- TODO: Keys are case sensitive. I will add a flag later
- TODO: Objects in Arrays are not properly parsed
- There are no types just now. Everything is a string. I might add a type system later. Especially for the array type, although I'm not quite sure how to design it. For now you'd have to explode an array yourself with StringField() and convert any numbers yourself as well.
- I put some comments, but not everything is commented, especially the latter part where the actual parsing happens. I will work on that.
- I am NOT an expert on json, nor PB. Please aid and help with tips, suggestions and pointing out coding mistakes wherever you can, if you like. Thank you.
Code: Select all
;============================================================
;= JSON:: Module for easy json access
;= Version: 0.1a
;= Author: Derren (german and english purebasic forum)
;= PB Version: 5.22 LTS x86 (written in)
;= Infothread: http://purebasic.fr/english/viewtopic.php?f=12&t=60656
;============================================================
DeclareModule JSON
#JSON_Node_Delimiter$ = "\" ; The delimiter of object levels, e.g. "." in Javascript or "->" in php.
; In PB the closest thing would be the "\" in structures, so that is what I'm using here but feel free to change it.
Declare eval(input.s) ; Should return 0. If not, there's an error in object depth, e.g. "{key:value,key2:{object}" (closing paranthesis missing)
Declare.s GET(key.s) ; Returns the value for the given key as a string
Enumeration
#Json_Type_Generic
#Json_Type_NULL
#Json_Type_Bool
#Json_Type_Num
#Json_Type_String
#Json_Type_Array
EndEnumeration
EndDeclareModule
Module JSON
Structure json
type.i ;type of value (see Enumeration in Declare block above). Not in use yet
Data.s ;the value as a string
key.s ;just stores the key of the map for when you iterate through the map with ForEach
EndStructure
;---private functions
Procedure.s _Strip(string.s) ;Trims spaces, tabs, encasing double quotes
string = Trim(string)
string = Trim(string, Chr(9))
string = Trim(string, Chr(34))
ProcedureReturn string
EndProcedure
;--- private declarations
NewMap JSON_Map_Internal.json()
;---public functions
Procedure.s GET(key.s)
Shared JSON_Map_Internal()
ProcedureReturn JSON_Map_Internal(key)\data
EndProcedure
Procedure eval(input.s)
Shared JSON_Map_Internal()
Protected *c.Character = @input ; Pointer with the size of a char at the adress of the input string ( = first char in string)
Protected objectLevel.i ; Depth of objects, i.e. {bla:"level_1", blub {meep:"level_2, another:"level_2"}}
Protected *key_begin = *c ; Marks the beginning of a key, based on the delimiters
Protected *key_end ; Marks the end of a key ^^
Protected *value_begin = *c ; Same for the value ^^
Protected *value_end ; ^^
Protected inString.i ; bool to determine if a delimiter is inside a string and thus should be ignored
Protected inArray.i ; bool to determine whether or not a comma is in an array or outside where it marks a new key pair
Protected keyExists.i ; This is set to true when a key exists before a value. An easy workaround for double values found when "}," is encountered
Protected KEY.s ; The actual key used to access a node. This variable is used to build the key which is then used in the map
Protected TYPE.i ; The type of the value
Protected counter.i ; counter variable used in For loop
Dim lastKeyOnLevel.s(1)
While *c\c ! 0 ; While EndOfString <> 0
Select *c\c
Case '"' ; Hitting ": Either entering or leaving string
inString = Abs(inString - 1) ; -> Alternate inside/outside state
Case '{'
If Not inString
*key_begin = *c
objectLevel +1
ReDim lastKeyOnLevel(objectLevel)
EndIf
Case '}'
If Not inString
*value_end = *c
objectLevel -1
EndIf
Case '['
If Not inString
inArray=#True
EndIf
Case ']'
If Not inString
inArray=#False
EndIf
Case ':'
If Not inString
*key_end = *c
*value_begin = *c
EndIf
Case ','
If Not inString And Not inArray
*value_end = *c
*key_begin = *c
EndIf
EndSelect
If *c = *key_end
*key_begin + SizeOf(Character)
lastKeyOnLevel(objectLevel) = _Strip( PeekS(*key_begin, (*key_end - *key_begin)/SizeOf(Character)) )
KEY=""
For counter = 1 To objectLevel - 1
KEY + lastKeyOnLevel(counter) + #JSON_Node_Delimiter$
Next
KEY + _Strip( PeekS(*key_begin, (*key_end - *key_begin)/SizeOf(Character)) )
keyExists = #True
EndIf
If *c = *value_end And keyExists=#True
*value_begin+SizeOf(character)
keyExists=#False
JSON_Map_Internal(KEY)\data = _Strip( PeekS(*value_begin, (*value_end - *value_begin)/SizeOf(Character)) )
JSON_Map_Internal(KEY)\key = KEY
EndIf
*c + SizeOf(Character) ; Next Character
Wend
ProcedureReturn objectLevel ;Should be 0. If not, the json string contained an error.
EndProcedure
EndModule
;-
;-EXAMPLE
Define json.s = "{" ;copied from Wikipedia and edited to add more variety
json + " 'Company': 'Xema',"
json + " 'Number': '1234-5678-9012-3456',"
json + " 'Exponent': 2e+6,"
json + " 'Currency': 'EURO',"
json + " 'Person': {"
json + " 'Name': 'Mustermann',"
json + " 'First Name': 'Max',"
json + " 'male': true,"
json + " 'Hobbys': [ 'Hiking', 'Golf', 'Reading' ],"
json + " 'Age': 42,"
json + " 'Children': [{name:julia},{name:hans}],"
json + " 'Spouse': null"
json + " }"
json + "}":ReplaceString(json, "'", Chr(34), #PB_String_InPlace)
JSON::eval(json)
Debug "Company: "+ JSON::GET("Company")
Debug "Number: "+ JSON::GET("Number")
Debug "Person: "+ JSON::GET("Person") + "<< empty, as it's an object and not a real value"
Debug "Person -> Name: "+ JSON::GET("Person\Name")
Debug "Person -> Hobbys: "+ JSON::GET("Person\Hobbys")
Debug JSON::GET("Person\Children\name") ;this is not a proper key, it should be an array index. This needs to be fixed