Version 19.0 includes an extension to ⎕JSON that provides the ability to export APL arrays which have a specific data structure that would not otherwise be recognised. The structures that have been selected for this special treatment are ones that are often used to represent tables or datasets. The extension allows ⎕JSON to render APL table representations in a format that would be expected by many other programming languages including JavaScript.
Datasets
The term dataset is used here to mean a collection of data, usually presented in tabular form. Each named column represents a particular variable. Each row corresponds to a given member of the dataset in question. It lists values for each of the variables, such as height and weight of an object.
Datasets are often represented in APL as a collection of variables.
Fields←'Item' 'Price' 'Qty' Items←'Knife' 'Fork' Price←3 4 Qty←23 45
As an aside, note that using this scheme each variable represents an inverted index into the dataset and enables rapid searches.
(Price<4)/Items ┌─────┐ │Knife│ └─────┘
A conventional way to represent this dataset is as a matrix:
Fields⍪⍉↑ Items Price Qty ┌─────┬─────┬───┐ │Item │Price│Qty│ ├─────┼─────┼───┤ │Knife│3 │23 │ ├─────┼─────┼───┤ │Fork │4 │45 │ └─────┴─────┴───┘
Another is as a 2-item vector containing the names of the fields and a matrix of their values:
(Fields (⍉↑Items Price Qty)) ┌────────────────┬────────────┐ │┌────┬─────┬───┐│┌─────┬─┬──┐│ ││Item│Price│Qty│││Knife│3│23││ │└────┴─────┴───┘│├─────┼─┼──┤│ │ ││Fork │4│45││ │ │└─────┴─┴──┘│ └────────────────┴────────────┘
A third way retains the inverted nature of the data structure, storing the values as a vector. The advantage of this structure is that it consumes significantly less memory compared to the matrix forms, because numeric columns are stored as simple numeric vectors.
(Fields (Items Price Qty)) ┌────────────────┬────────────────────────┐ │┌────┬─────┬───┐│┌────────────┬───┬─────┐│ ││Item│Price│Qty│││┌─────┬────┐│3 4│23 45││ │└────┴─────┴───┘│││Knife│Fork││ │ ││ │ ││└─────┴────┘│ │ ││ │ │└────────────┴───┴─────┘│ └────────────────┴────────────────────────┘
In JSON, these three data structures are all expressed as follows:
[ { "Item": "Knife", "Price": 3, "Qty": 23 }, { "Item": "Fork", "Price": 4, "Qty": 45 } ]
The extension to ⎕JSON has been implemented by introducing a wrapper.
Wrappers
A wrapper is an enclosed vector of the form:
⊂(code special)
The nature of the special data structure is identified within the wrapper by a leading numeric code. Code 1 is used to identify JSON values such as null
, true
and false
. Codes 2, 3 and 4 are used to identify different forms of datasets.
This wrapper mechanism has been chosen to identify special treatment because a scalar enclosure cannot be represented in JSON/JavaScript.
A wrapper may be specified directly in the right argument to ⎕JSON and/or as part of the array structure specified by the right argument, as a sub-array or in a namespace. This allows a special array to be processed appropriately as part of a general data structure that is to be rendered in JSON notation.
Examples
Fields,[1]↑[1]Items Price Qty ┌─────┬─────┬───┐ │Item │Price│Qty│ ├─────┼─────┼───┤ │Knife│3 │23 │ ├─────┼─────┼───┤ │Fork │4 │45 │ └─────┴─────┴───┘ ⎕JSON ⊂ 2 (Fields,[1]↑[1]Items Price Qty) [{"Item":"Knife","Price":3,"Qty":23},{"Item":"Fork","Price":4,"Qty":45}]
Note that if you omit the wrapper the operation fails:
⎕JSON Fields,[1]↑[1]Items Price Qty) DOMAIN ERROR: JSON export: the right argument cannot be converted (⎕IO=1) ⎕JSON Fields,[1]↑[1]Items Price Qty) ∧
Further Examples
⎕JSON ⊂ 3 ((↑[1]Items Price Qty)Fields) [{"Item":"Knife","Price":3,"Qty":23},{"Item":"Fork","Price":4,"Qty":45}]
⎕JSON ⊂ 4 ((Items Price Qty)Fields) [{"Item":"Knife","Price":3,"Qty":23},{"Item":"Fork","Price":4,"Qty":45}]
Note that if you omit the wrapper, the operation generates a different result.
⎕JSON ((Items Price Qty)Fields) [[["Knife","Fork"],[3,4],[23,45]],["Item","Price","Qty"]]
Selection
For codes 2, 3 and 4 the extension also provides the facility to optionally select elements of the dataset, so the array may contain 2, 3 or 4 items:
⊂(code dataset {records} {fields})
where records and fields are integer indices that select which fields and which records are to be exported. The following example selects the first record and the first and third fields (Items and Qty)
⎕JSON⊂4 ((Items Price Qty)Fields)1(1 3) [{"Item":"Knife","Qty":23}]
Namespaces and Sub-Arrays
Wrappers in namespaces and sub-arrays are recognised for special treatment.
Example
ns.Items←'Fork' 'Knife' ns.Price←3 4 ns.Qty←23 45 ns.(ds←⊂4(⌽('Item' 'Price' 'Qty')(Items Price Qty))) ⎕JSON ns {"Items":["Knife","Fork"],"Price":[3,4],"Qty":[23,45], "ds":[{"Item":"Knife","Price":3,"Qty":23}, {"Item":"Fork","Price":4,"Qty":45}]}
a←'the' 'answer' 'is' 42 a[3]←⊂ns.ds ⎕JSON a ["the","answer",[{"Item":"Knife","Price":3,"Qty":23},{"Item":"Fork","Price":4,"Qty":45}],42]
Wrappers for special JSON values
Previously, the enclosed character vectors 'null', 'false' and 'true' were used to represent the JSON values null, false and true respectively. This is still supported, and indeed remains the only way to represent these value on import, but for export the wrapper mechanism may be used instead.
Example
⎕JSON⊂¨(1 'null')(1 'true')(1 'false') [null,true,false]