MSX JSON Parser
A small library to parse JSON files under MSX BASIC.
Rationale
There are a lot of network cartridges available for MSX. You may want to use them to connect to the many REST endpoints around us, but most only communicate in JSON. This lib provides JSON parsing capabilities to fill this gap.
The lib was designed to blend with MSX BASIC idiomatically, and it uses a macro command interface just like PLAY and DRAW to achieve this goal.
Features
- Install with
BLOAD "JSON.BIN",R
. This will install three API calls into theDEFUSR
user routines. - Optimized for size. The entire lib fits under 1kb.
- Not relocatable. Provided binary loads from
0xD000
, but you can change theORG
in the source file if you need a different start address. - JSON file must be loaded into BASIC-visible memory (
0x8000-0xFFFF
). - JSON parsing follows RFC 7159 to the letter. Take care with caveats of the format (integers may not start with a 0, strings must always use double-quotes instead of single quotes, trailing commas before closing an array are not allowed, etc).
- No extra memory beyond the lib itself. No additional data structures are created when parsing the JSON, to minimize memory usage. However, all lib calls are O(n) in the size of the JSON file.
- Unit-tested to ensure lib quality.
Usage
There are three calls available in the API:
S=USR(AD)
: Sets JSON start addressAD
.T=USR1(Q$)
: Gets the type of the JSON token pointed by queryQ$
.V$=USR2(Q$)
: Gets the value of the JSON token pointer by queryQ$
as a string.
Detailed usage for each call is as follows:
S=USR(AD)
: Sets JSON start address
Sets the JSON start address as the given integer AD. Validates the JSON and returns the validation status. Example:
IF USR(&H9000)=0 THEN PRINT "NOT A VALID JSON FILE"
Status code on return follows the MSX BASIC boolean convention:
- 0 = JSON is invalid
- -1 = JSON is valid
This call is always required. Further calls to USR1
and USR2
will fail if there is not a valid JSON file set up by this call.
Errors:
- Returns
Type mismatch
ifAD
is not an integer.
T=USR1(Q$)
: Gets type of JSON token
Gets the type of JSON token pointed by query Q$
. The return code is an integer whose meaning is:
- 0 = nothing found
- 1 = object (dict)
- 2 = array
- 3 = string
- 4 = number
- 5 = true
- 6 = false
- 7 = null
Suppose you have made a query to your drone api and received this JSON:
{
"info": {
"name": "Cool Drone",
"origin": "China"
},
"hasCamera": true,
"hasMicrophone": false,
"password": null,
"position": [1.0, 2.0, -1.0]
}
These are some sample queries:
PRINT USR1("&info")
1
PRINT USR1("&info&name")
3
PRINT USR1("&info&origin")
3
PRINT USR1("&hasCamera")
5
PRINT USR1("&hasMicrophone")
6
PRINT USR1("&password")
7
PRINT USR1("&position")
2
PRINT USR1("&position#0")
4
PRINT USR1("&position#4")
0
PRINT USR1("&unknown")
0
The format for the query Q$
is described below.
Errors:
- Returns
Type mismatch
ifQ$
is not a string. - Returns
Invalid function call
ifQ$
is not in the format expected. - Returns
Invalid function call
ifUSR(AD)
was not called first.
V$=USR2(Q$)
: Gets value of JSON token
Gets the value of the JSON token pointer Q$
. The value is always returned as a string, even when the JSON token type is different. For instance, the number 10
will be returned as a string "10"
. To differentiate between 10
and "10"
, you need to check the type returned by USR1(Q$)
.
Some sample queries for the JSON above:
PRINT USR1("&info&name")
Cool Drone
PRINT USR1("&info&origin")
China
PRINT USR1("&hasCamera")
true
PRINT USR1("&hasMicrophone")
false
PRINT USR1("&password")
null
PRINT USR1("&position#0")
1.0
If the string length is greater than 255 chars, then only the first 255 chars will be returned (this is limitation of MSX BASIC).
Errors:
- Returns
Type mismatch
ifQ$
is not a string. - Returns
Invalid function call
ifQ$
is not in the format expected. - Returns
Invalid function call
ifUSR(AD)
was not called first. - Returns
Invalid function call
if the JSON type is not string, number, true, false or null.
Query format
There are currently three commands in the macro language used by the queries:
#N
: Traverse to theN
-th element of this collection (object or array). The numbering is 0-based.$
: Traverse to the value pointed by the current object key.&KEY
: Traverse to the value pointed by the given key.
Let's again consider the JSON file above. The #N
command retrieves the N
-th element of an array or object:
PRINT USR2("#0")
info
PRINT USR2("#1")
hasCamera
PRINT USR2("&position#0")
1.0
PRINT USR2("&position#1")
2.0
PRINT USR2("&position#2")
-1.0
For objects, the element pointed is always the key. If you want the value pointed by the key, you need the command $
:
PRINT USR2("#0")
info
PRINT USR2("#0$#0")
name
PRINT USR2("#0$#0$")
Cool Drone
If the object or array is shorter than the query, USR1(Q$)
will return zero, and USR2(Q$)
will return Invalid function call
. You can use this to find the length of an array:
PRINT USR1("&position#0")
4
PRINT USR1("&position#1")
4
PRINT USR1("&position#2")
4
PRINT USR1("&position#3")
0
Spaces are allowed between #
and the number, to facilitate BASIC usage with STR$
:
PRINT USR2("# 0")
info
The &
command retrieves the value pointed by a key. No spaces are allowed between &
and the key, as the key may contain spaces. Keys containing the chars $&#
are not allowed. String comparison is a naive byte-oriented comparison, escapes and unicode will not work with this command.
PRINT USR2("&info&name")
Cool Drone
Credits
Written by Ricardo Bittencourt in 2017. Free for commercial usage (a note in the credits is appreciated).