- DelphiTools - https://www.delphitools.info -

Spotlight on dwsJSON

[1]dwsJSON is a unit that supports JSON [1] parsing/creating, it’s part of DWScript [2] but relatively “standalone”, in that if you add it in your Delphi (or FPC) projects, it won’t pull the whole of DWScript library, and thus can be used anywhere you need.

The JSON parser in dwsJSON generates an object tree based on TdwsJSONValue (and its subclasses), you can also create such a tree from scratch and use it to spit out JSON. There is also a low-level JSON writer for fast, write-only streaming. All classes support full Unicode (in Delphi) as well as very long strings (like a base64 streamed image).

dwsJSON is rather fast, especially compared to Delphi’s DBXJSON [3], don’t be surprised if it parses data 15 to 30 times faster (f.i. on JSON.org examples [4]). And the ratio can go even higher on larger JSON datasets. It is also more resilient to bad input, less buggy ( 😉 ), and should use less memory in just about any usage scenario. It also is about twice faster than the last version (as of this writing) of SuperObject [5], which is itself quite efficient.

Parsing JSON

Let’s suppose you have the following JSON data

{
   "Hello" : [
      "here",
      {
         "There" : 456;
      }
   ],
   "World" : 123
}

and you loaded it in the rawJSON variable, you can parse it with

var json : TdwsJSONValue;
...
json := TdwsJSONValue.ParseString(rawJSON);

That’s all.

Accessing the JSON data

You can access the data using explicit methods and properties (see the source and unit tests), or use the short forms:

WriteLn('Hello has ', json['Hello'].ElementCount, ' elements');

WriteLn('The first element has a ValueType of ', GetEnumName(TypeInfo(TdwsJSONValueType), Ord(json['Hello'][0].ValueType)));
WriteLn('The second element class name is ', json['Hello'][1].ClassName);

WriteLn('The first element has a first field named "', json['Hello'][0].Names[0], '"');
WriteLn('and its JSON representation is ', json['Hello'][0]['here'].ToString);

WriteLn('The value of the DoesNotExists field is "', json['DoesNotExists'].ToString, '"');
WriteLn('and its type is ', GetEnumName(TypeInfo(TdwsJSONValueType), Ord(json['DoesNotExists'].ValueType)));

WriteLn('Also World = ', json['World'].ToString);

will give the following output:

Hello has 2 elements
The first element has a ValueType of jvtObject
The second element class name is TdwsJSONImmediate
The first element has a first field named "here"
and its JSON representation is {"There":456}
The value of the DoesNotExists field is ""
and its type is jvtUndefined
Also World = 123

One thing of note is that TdwsJSONValue allows to navigate in nodes that do NOT exist in the JSON (ie. nil objects in Delphi), they’ll have a ValueType of jvtUndefined, if you query their value, you’ll get an empty string, a zero, etc.

This can be particularly convenient when the JSON has optional portions (common in JSON), as it allows to drastically cut down on the “if” testing. For instance you can write

json['DoesNotExists'].ElementCount

even if json[‘DoesNotExists’] is nil, ie. if your array is missing in the JSON, ElementCount will be zero, and if you write

json['DoesNotExists'][2]['Field']

it will return a nil TdwsJSONValue, whose ValueType will be jvtUndefined.

Altering or writing JSON

You can alter an existing (or newly generated) JSON tree by using the AddXxxx methods and assign values using the AsXxxx. You remove elements through the DetachChild method, which can be used to delete elements, or to split a monolithic JSON into simpler sub-trees.

Another option to generate JSON is to use the lower-level TdwsJSONWriter, which is a simple streamer (so the output JSON will only be correct if you write everything needed). It has a TdwsJSONBeautifiedWriter subclass, which instead of generating a “tight” JSON will introduces tabs, spaces and new lines to make the output more palatable when it’s intended to be human-readable.

The TdwsJSONWriter provides a set of WriteXxxx methods, along with Begin/End Object/Array methods, so can be easily adapted to most serialization frameworks.

Licensing

Like the rest of DWScript, dwsJSON is under MPL 1.1 [6], ie. free to use in both open-source and commercial software, as long as you honor the license terms: the DWScript project or dwsJSON must be “conspicuously” mentioned, and you have to publish modifications to the code (if you make any, you don’t have to publish anything if you use it verbatim). If you’re interested in using it under other terms, feel free to contact me.