xjson.tcl is a pure-Tcl library that validates, collects, composes JSON data against supplied schemas and to/from Tcl data.
The library comes with a huge number of test cases.
Comments and further testcases welcome.
From the manpage:
This package is a set of extended JSON functions for Tcl. It allows decoding, encoding, and pretty-printing of JSON structures from Tcl structures and vice versa. In addition, decoded JSON that was created by functions outside of this package may be recoded. A set of diff and patch functions tailored to JSON allows to track changes in compli- cated JSON structures easily.
The main feature of this package however are two class factories that produce itcl classes that construct validator and data collector/composer objects. Those objects take a schema in a simple nested list syntax on construction, so they are then prepared for validating against that schema again and again. The schema may also feature various operators that manipulate the validated data so that further configuration specific processing (e.g. data formatting) can be specified in the realm of the administrator rather than the programmer.
Schemas may be nested and libraries of commonly used collector/composer objects and their schemas can be constructed easily.
The objects also feature automatic sandboxing, so they can specify Tcl code for doing more complicated tests or data manipulations. In addition, the programmer may define their own barebone, non-sandboxed methods when creating a new class with the class factory. For sandboxes and those methods, an additional security mechanism exists. Schemas may be marked as trusted (programmer/administrator supplied) and only such trusted schemas may use Tcl code or user-defined methods that are marked unsafe.
The procedures and objects return error messages that indicate the offending data and the non-matching schema in a human-readable, pretty-printed way. They are fit for supplying them to the interface user, which is usually a programmer or administrator of an another software interfacing the software using xjson.
From the manpage:
Create a collector class with the class factory and the builtin methods. Even for advanced usage, you only ever need to do this once. More than once only if you want to create multiple collector classes with a different set of builtin and your own collection methods, or different options.
% ::xjson::makeCollectorClass ::myCollectorClass
Create a collector object with a schema. You need to do this once per different schema you want to use.
% set ::mycollector [::myCollectorClass #auto { object { "articles" { dictby "id" {array -max 100 {object { "id" integer "extra" {default false boolean} "name" string "price" {format "%.2f" number} }}} } } }]
Feed JSON data into the decoder, and the decoded json data into the collector object.
% $::mycollector collect [::xjson::decode { { "articles": [ { "id": 101, "name": "Pizzapane bianca", "price": 4.95 }, { "id": 120, "name": "Pizza Regina", "price": 9.8 }, { "id": 139, "name": "Wunschpizza", "price": 12.70 }, { "id": 201, "name": "Rucola", "extra": true, "price": 1 } ] } }] articles {101 {extra false name {Pizzapane bianca} price 4.95} 120 {extra false name {Pizza Regina} price 9.80} 139 {extra false name Wunschpizza price 12.70} 201 {extra true name Rucola price 1.00}}
Create a composer class with the class factory and the builtin methods. Even for advanced usage, you only ever need to do this once. More than once only if you want to create multiple composer classes with a different set of builtin and your own composer methods, or different options.
% ::xjson::makeComposerClass ::myComposerClass
Create a composer object with a schema. You need to do this once per different schema you want to use.
% set ::mycomposer [::myComposerClass #auto { array { object { id integer name string } } }]
Feed Tcl data into the composer object, and the result into the encoder.
% ::xjson::encode [$::mycomposer compose {{id 7 name foo} {id 3 name bar}}] [ { "id": 7, "name": "foo" }, { "id": 3, "name": "bar" } ]
2022-02-02 APN Looks good. May I ask why you chose to use Itcl as opposed to Tcl's built-in oo system? Is there any specific feature of Itcl that is needed? Only asking because it introduces an additional dependency, that too on a package with no official owner afaik.
2022-02-02 Janka I had the impression TclOO is meant for building OO packages as e.g Itcl, not meant for using it directly. Also, I'm a long time Tcl user and Itcl is what I know best. And it's part of the core since Tcl-8.6, isn't it? — Should I use TclOO instead? It's not a huge problem to change that as the classes are made by those two class factory methods and the features used are super basic. Only object variables and class-wide variables, object methods and class-wide procedures.