#+TITLE: JSON meta library * What is a meta library? The purpose of this library is to provide a high level abstraction layer for handling JSON. * Why? If one wants to write portable Scheme which does some JSON processing, one must either: - Abandon hope of being portable, and depend on an implementation specific library. - Use (possibly by writing) a portable JSON library which is redundant, and may conflict with other libraries that expect parsed JSON to be represented in a certain way. - Write a bunch of cond-expands to act as a portability layer, and use different JSON libraries in different contexts. This library aims to be an implementation of the latter, which you can just plug in as a dependency, instead of writing your own. * API ** Values ~json-null~: This is a constant value which represents null in JSON. ** Constructors ~(json-object pairs ...)~: Constructs a new JSON object. ~pairs~ must satisfy ~pair?~, with a ~car~ that satisfies ~symbol?~, and a ~cdr~ that satisfies ~json-value?~. It is an error to construct a ~json-object~ that contains duplicate keys. An alist can be converted to a JSON object like so: ~(apply json-object alist)~ ~(json-list objs ...)~: Constructs a new JSON list. ~objs~ must satisfy ~json-value?~. A list can be converted to a JSON list like so: ~(apply json-list some-list)~ *** Syntax ~json~: Constructs a new JSON value. The ~json~ macro has ~list~ and ~object~, ~null~, ~true~, and ~false~ keywords, and handles quoting the appropriate terms. *Example*: #+begin_src scheme (json (object (x . 2) (y . (list "hello" 1 true null (object (a . (object))))))) #+end_src ~define-json-record-type~: Creates a new record-type with an additional procedure definition that will convert a string to an instance of the record. Objects that were not constructed via a record constructor, but that have at least all of the keys required to construct the record, may be transparently treated as instances of the record-type. *Example*: #+begin_src scheme (define-json-record-type (point x y) string->point point? (x point->x) (y point->y set-y!)) (define a (point 1 2)) (define b (json (object (x . 3) (y . 4)))) (define c (string-point "{\"x\": 5, \"y\": 6}")) (and (point? a) (point? b) (point? c)) ; => #t (eq? 3 (point->x b)) ; => #t (set-y! a 5) (set-y! b 5) (eq? (point->y a) (point->y b)) ; => #t #+end_src ** Predicates ~(json-value? obj)~: Returns true if ~obj~ is a value that can be serialized as JSON. This is true for values returned by the [[Constructors][constructors]], ~json-null~, numbers, strings, and booleans. ~(json-object? obj)~: Returns true if ~obj~ is a value that can be serialized as a JSON object. Either the result of deserializing a string formatted as a JSON object, the ~json-object~ constructor, or a constructor created as the result of a ~define-json-record-type~ expression. ~(json-list? obj)~: Returns true if ~obj~ is a value that can be serialized as a JSON list. Either the result of deserializing a string formatted as a JSON list, or from the ~json-list~ constructor. ~(json-null? obj)~: Returns true if ~obj~ is ~json-null~. ~(json-object-contains-key? obj key)~: Returns true if ~obj~ contains the ~key~. ** Serialization ~(json->string obj)~: Convert a ~json-value?~ to a JSON formatted ~string?~. ~(string->json str)~ Converts a ~string?~ into a ~json-value?~ ** Selectors ~(json-object-ref obj key)~: Returns the ~json-value?~ associated with ~key~ in ~obj~. ~key~ should be a ~symbol?~. The behavior when ~key~ is not found in ~obj~ can be controlled with the ~json-key-not-found~ parameter. The value of ~json-key-not-found~ should be a procedure of no arguments, and the return value of the procedure is returned, if any exists. The default behavior of ~json-key-not-found~ will return nothing to its continuation. ~(json-list-ref lst i)~: Returns the ~json-value?~ at index ~i~ of ~lst~. ~(json-list-length lst)~: Returns the length of ~lst~. ~(json-ref json ref refs ...)~: General JSON reference accessor. If ~json~ is a ~json-list?~ and ~ref~ is a ~number?~, get the element at that index of the list. If ~json~ is a ~json-object?~ and ~ref~ is a ~symbol?~, get the value associated with that key in the object. If there are more ~refs ...~, they are recursively applied. ** Conversion ~(json-object->alist obj)~: Convert ~obj~ to a ~list?~ of ~pair?~ where the ~car~ of each pair is a ~symbol?~ and the ~cdr~ is the associated ~json-value?~. ~(json-list->list lst)~: Convert ~lst~ to a ~list?~ of ~json-value?~. ** Examples #+begin_src scheme (json-list->list (apply json-list lst)) ; => lst #+end_src #+begin_src scheme (json-object->alist (apply json-object alist)) ; => alist #+end_src #+begin_src scheme (json-null? (string->json "null")) ; => #t #+end_src #+begin_src scheme (json-object-contains-key? (json-object '(a . 5) `(b . ,(json-list 1 2 3))) 'a) ; => #t (json-object-contains-key? (json-object '(a . 5) `(b . ,(json-list 1 2 3))) 'c) ; => #f #+end_src #+begin_src scheme (json-ref (json-object `(x . ,(json-list "hello" "world"))) 'x 0) ; => "hello" #+end_src #+begin_src scheme (import (srfi 210)) (case-receive (json-ref (json-object `("x" . ,(json-list "hello" "world"))) "x") (() 'not-found) ((x) (json-list? x))) ; => #t (case-receive (json-ref (json-object `("x" . ,(json-list "hello" "world"))) "y") (() 'not-found) ((x) (json-list? x))) ; => not-found #+end_src * Feedback Please email me at: contact (at) robbyzambito (dot) me if you have any suggestions or would like for me to add support for another JSON library.