The JSON LotusScript Classes project was a great project but kinda slow, it parsed 15k of text in 0.5 seconds. Or 1MB of JSON in 161 seconds.
This code does that on 0.1 second, and 1 MB of JSON in under 7 seconds, making it much more useful.
Changes from v2.7 through v4.1:
- Tried to fail faster, and be more resilient, and supply better error messages. The returned JSONValue from NotesJSONReader.Parse can now be of type typeJSONError, with an error string (JSONValue.Get will output a Variant of type String with the error message). Various improvements led to a parse time of around 2 seconds for a string of 1M characters.
- A bug in NotesStream was uncovered by a user of the classes. It surfaces when NotesStream.WriteText is used with long lines of UTF-8 text containg multi-byte characters. After a while, I found a Japanese technote ( https://support.hcltechsw.com/csm?id=kb_article&sys_id=b0f6f23f8784cd185440c9d8cebb35ef ) mentioning SPR# KKOOBZ9B2E .
In that technote the NotesStream call errors after around 10000 characters. In the Czech character case it bugged after around 27000 characters.
HCL is working on it, in the meantime I employ a workaround, I simply cut up the calls to WriteText to be 8000 or shorter.
Also, the \u Unicode escape code now supports code points after U+FFFF, which can be specified as a surrogate pair. I also uncoverd that the documentation for the LotusScript Uni and UChr$ functions is incorrect, the range of Long values they accept and produce is not 0-65535 but 0-1114111. I realized that the assumption I made that NotesStream.Read returns an array of Byte which can be turned into 16-bit Unicode values is wrong. Luckily, I just implemented the \u decode handling the same case.
To use the parsing classes, you need at three LotusScript libraries: lsAgentLog, lsJSONValue and lsJSONReader
Two versions are included: the entire dev thing with a lot of test code, and a release version with a more minmal set.
Changes from the v2.4 to v2.7:
NOTE: the output has changed to be significantly easier to use and debug!
Feedback resulted in a rewrite of the generated output. The variant business was confusing when debugging, and Variants are also wasteful when storing them in lots of objects. So a new class was devised: JSONValue.
The Parse method now always returns a JSONValue object. The JSONValue object has a valueType property, which must be specified to the constructor of the JSONValue class if you create your own.
The possible values are available as constants: typeJSONNull, typeJSONFalse, typeJSONTrue, typeJSONNumber, typeJSONString, typeJSONArray, typeJSONObject.
These have been implemented as the characters n, f, t, #, S, A, O to make debugging easier.
The JSON values null, false and true are only stored as their type, a JSON number is stored in a Double, and a JSON string in a String.
The JSON Object and Array values are stored in a List of JSONValue objects.
The old JSONArray object did not have a method to access its elements. This is now provided by the method GetItemValue, which is a valid method for JSON array and object type JSONValue objects to acces their elements. Note that the array is zero based, and GetItemValue requires a Long in the range of 0..(Count-1). An error will be raised if an attempt is made to provide invalid arguments.
The JSON specification allows the name of a name/value pair in a JSON object to be the empty string. SInce a LotusScript List does not allow that, a translation is made for the name being a single double quote (cQuote constant), which is an illegal string value in the JSON specification. GetItemValue accepts an empty string and applies the translation. If a the JSON Value object for name/value pair is requested with a name that is not present an error will be raised.
The method isElement can be used to test if a name or array element is present
Stricter adherance to JSON as defined by JSON.org by:
- the values false, true and null are not assumed if the first character is encountered. they must now be spelled correctly.
- a name in a name/value pair in a JSON object must followed by a colon.
The bug of an empty JSON object causing an error has been fixed.
Please report bugs and remarks. It may take a while, but I try to answer!
Changes from the original for v2.4:
earlier versions were using a base class for error reporting.
this has been reintroduced.
Also tried to clean up error reporting a bit (not too verbose).
This also cleaned up all the built up variant arrays and objects, because the error-reporing variables were clogging up the output.
Also moved more or less static objects to the base class.
Moved static members to global variables
Main change: put string into NotesStream, and read in blocks of 65534 bytes, converting them two by two (UTF16) into Longs and comparing them to constants with the Unicode codes.
At first i converted the entire string into a two dimensional array of long, but I soon realized that would lead to memory problems. Luckily the object oriented structure enabled me to switch to read the pieces on demand.
Tried to adress all the defects reported on OpenNTF
Did not do anything with the date request by Cesar Mugnatto
Main feature: Parse speed. parses 15 k JSON string in approx 100 msec, 1 MB of JSON under 7 seconds, a 23 fold performance gain!
TODO: JSONWriter classes rewrite.
I strongly suspect that a rewrite of the Writer parts to use a global stringbuilder class for all string concatenates will speed it up nicely. However, I'm pressed for time now. It will have to wait.
Basically it is just a faster version of the original JSON LotusScript Classes project, so head over there to get the rest of the documentation.