| |
| |
| |
| |
|
|
| (in-package :json) |
|
|
| (defun range-keys (var-keys) |
| (loop for var-key in var-keys |
| for (primary-key . subkey) = |
| (destructuring-bind (var . key) var-key |
| (let ((dot (position #\. key :test #'char=))) |
| (if dot |
| (cons (subseq key 0 dot) (cons var (subseq key (1+ dot)))) |
| (cons key var)))) |
| for subkeys-of-primary = |
| (assoc primary-key subkeys :test #'string=) |
| if subkeys-of-primary |
| do (push subkey (cdr subkeys-of-primary)) |
| else |
| collect (cons primary-key (list subkey)) into subkeys |
| finally (return subkeys))) |
|
|
| (defun json-bind-level-customizations (level-keys value-required |
| decoder validator |
| key-handler value-handler pass) |
| (loop for (key . subs) in (range-keys level-keys) |
| with subkeys and vars-to-bind |
| do (loop for sub in subs |
| initially (setq subkeys nil vars-to-bind nil) |
| if (consp sub) do (push sub subkeys) |
| else do (push sub vars-to-bind)) |
| collect |
| `((string= key ,key) |
| (set-custom-vars |
| :internal-decoder |
| ,(if (endp subkeys) |
| (if vars-to-bind decoder validator) |
| (cons 'custom-decoder |
| (json-bind-level-customizations |
| subkeys vars-to-bind decoder validator |
| key-handler value-handler pass))) |
| :object-value |
| (lambda (value) |
| (declare (ignorable value)) |
| ,@(loop for var in vars-to-bind collect `(setq ,var value)) |
| ,(if value-required `(funcall ,value-handler value))))) |
| into match-clauses |
| finally |
| (return |
| `(:object-key |
| (lambda (key) |
| (let ((key (funcall *json-identifier-name-to-lisp* key))) |
| (cond |
| ,@match-clauses |
| (t ,(if value-required |
| (list 'set-custom-vars |
| :internal-decoder decoder |
| :object-value value-handler) |
| (list 'set-custom-vars |
| :internal-decoder validator |
| :object-value pass))))) |
| ,(if value-required |
| `(funcall ,key-handler key))))))) |
|
|
| (defmacro json-bind ((&rest vars) json-source &body body) |
| (let-gensyms (decoder validator value-handler key-handler pass) |
| (let ((vars-tmp (loop repeat (length vars) collect (gensym)))) |
| `(let (,@vars-tmp (,pass (constantly t))) |
| (let ((,validator |
| (custom-decoder |
| :beginning-of-object ,pass :object-key ,pass |
| :object-value ,pass :end-of-object ,pass |
| :beginning-of-array ,pass :array-member ,pass |
| :end-of-array ,pass :beginning-of-string ,pass |
| :string-char ,pass :end-of-string ,pass |
| :internal-decoder 'decode-json)) |
| (,decoder (current-decoder)) |
| (,key-handler *object-key-handler*) |
| (,value-handler *object-value-handler*)) |
| (declare (ignorable ,decoder ,key-handler ,value-handler)) |
| ,(if (null vars) |
| `(decode-json-from-source ,json-source ,validator) |
| `(bind-custom-vars |
| (,@(json-bind-level-customizations |
| (loop for var in vars for var-tmp in vars-tmp |
| collect (cons var-tmp (symbol-name var))) |
| nil decoder validator |
| key-handler value-handler pass) |
| :aggregate-scope |
| (union *aggregate-scope-variables* |
| '(*object-key-handler* |
| *object-value-handler* |
| *internal-decoder*))) |
| (decode-json-from-source ,json-source)))) |
| (let ,(mapcar #'list vars vars-tmp) |
| ,@body))))) |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|