File size: 6,053 Bytes
43203b4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | ;;;; -*- Mode: LISP; Syntax: ANSI-COMMON-LISP; Base: 10; Package: JSON -*-
;;;; Copyright (c) 2006-2008 Henrik Hjelte
;;;; All rights reserved.
;;;; See the file LICENSE for terms of use and distribution.
(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)))))
;;; Old code:
;;; helpers for json-bind
;(defun cdas(item alist)
; "Alias for (cdr (assoc item alist))"
; (cdr (assoc item alist)))
;
;(defun last1 (lst)
; (first (last lst)))
;
;(defmacro assoc-lookup (&rest lookuplist)
; "(assoc-lookup :x :y alist) => (cdr (assoc :y (cdr (assoc :x alist))))"
; (let ((alist-form (last1 lookuplist))
; (lookups (reverse (butlast lookuplist))))
; (labels ((mk-assoc-lookup (lookuplist)
; (if lookuplist
; `(cdas ,(first lookuplist) ,(mk-assoc-lookup (rest lookuplist)))
; alist-form)))
; (mk-assoc-lookup lookups))))
;
;(defmacro json-bind (vars json-string-or-alist &body body)
; (labels ((symbol-as-string (symbol)
; (string-downcase (symbol-name symbol)))
; (split-by-dots (string)
; (loop for ch across string
; with x
; with b
; do (if (char= #\. ch)
; (progn
; (push (concatenate 'string (nreverse b)) x)
; (setf b nil))
; (push ch b))
; finally (progn
; (push (concatenate 'string (nreverse b)) x)
; (return (nreverse x)))))
; (lookup-deep (variable)
; (mapcar (lambda (nm) `(json-intern ,nm))
; (split-by-dots (symbol-as-string variable)))))
; (let ((a-list (gensym)))
; `(let ((,a-list (if (stringp ,json-string-or-alist)
; (decode-json-from-string ,json-string-or-alist)
; ,json-string-or-alist)))
; (let ,(loop for v in vars collect `(,v (assoc-lookup ,@(lookup-deep v)
; ,a-list)))
; ,@body)))))
|