anchor.diff:           collect `(assoc (json-key (quote ,f))
                            ,alist))))
   
  -(defmacro copy-slots-from-alist ((alist obj) &rest fields)
  -  "Set the FIELDS slots of OBJ from the JSON-decoded object ALIST."
  -  `(progn
  -     ,@(loop for f in fields
  -          collect `(setf (slot-value ,obj (quote ,f))
  -                         (cdr (assoc (json-key (quote ,f))
  -                                     ,alist))))))
  -
  -(defmacro maybe-copy-slots-to-alist ((obj alist) &rest fields)
  -  "Add entries to ALIST from the slots of OBJ."
  -  `(progn
  -     ,@(loop for f in fields
  -          collect `(push (cons (json-key (quote ,f))
  -                               (slot-value ,obj (quote ,f)))
  -                         ,alist))))
  +(defun get-field (key alist &rest args)
  +  "Return either the value of the field named KEY from the decoded
  +json object ALIST, or NIL. Additional arguments will be passed to
  +ASSOC."
  +  (let* ((key (json-key key))
  +         (cell (apply #'assoc (cons key (cons alist args)))))
  +    (and cell (cdr cell))))
  +
  +;;; Error conditions
  +(define-condition invalid-json-obj ()
  +  ((type :initarg :type)
  +   (json :initarg :json)))
  +
  +(define-condition invalid-call-object ()
  +  ((obj :initarg :obj)))
  +
  +(define-condition invalid-result-obj ()
  +  ((obj :initarg :obj)))
   
   ;;; Call object, one per method call
  +;; TODO: Find a more stringent definition for arglist ('(list string)?)
  +(deftype arglist () `list)
  +
   (defstruct rpc-call
     "A single method invocation."
  -  (service)
  -  (method)
  -  (args)
  -  (id))
  +  (service "" :type string :read-only t)
  +  (method "" :type string :read-only t)
  +  (args '() :type arglist :read-only t)
  +  (id nil :read-only t))
   
   (defun valid-call-p (obj)
     "Returns T if the object is a valid CALL object."
  
positive.code:   (and (listp obj)
         (every #'rpc-call-p obj)))
  
  (deftype rpc-request () '(and list (satisfies rpc-request-p)))
  
  (define-condition invalid-call-object ()
    ((obj :initarg :obj)))
  
  (defun marshall-request (obj)
    (check-type obj rpc-request)
    (when (not (every #'valid-call-p obj))
      (error 'invalid-call-obj :obj (find-if-not #'valid-call-p obj)))
    (json:with-array ()
      (loop for call in obj do
           (json:as-array-member ()
             (marshall-call call)))))
  
  (defun json-is-request (json-obj)
    "Return T if a REQUEST object could be unmarshalled from JSON-OBJ."
    (and (listp json-obj)
         (every #'json-is-call json-obj)))
  
  (defun intermediate-to-request (obj)
    "Convert an intermediate-format object to a request object."
    (mapcar #'intermediate-to-call obj))
  
  (defun unmarshall-request (json-string)
    "Unmarshall a REQUEST object from the decoded JSON object JSON-OBJ."
    (let ((json-obj (json:decode-json-from-string  json-string)))
      (when (not (json-is-request json-obj))
        (format *debug-io* "Invalid rpc-request json object ~S~%" json-obj)
        (error 'invalid-json-obj :type 'rpc-request :json json-obj))
      (intermediate-to-request json-obj)))
  
  ;;; Response, a list of RESULTs
  (defun rpc-response-p (obj)
  
negative.code: 
  (deftype rpc-response () '(and list (satisfies response-p)))
  
  ;; TODO: as convenient as this is, it seems like marshall-* should
  ;; return a string, rather than writing to json:*json-output*
  (defun marshall-response (obj)
    "Marshall the RPC-RESPONSE object OBJ to JSON."
    (check-type obj rpc-response)
    (json:with-array ()
      (loop for result in obj
         do (json:as-array-member ()
              (marshall-result result)))))
  
  (defun unmarshall-response (json-string)
    "Unmarshall a RESPONSE object from the decoded JSON object JSON-STRING."
    (let ((json-obj (json:decode-json-from-string json-string)))
      (unless (listp json-obj)
        (error 'invalid-json-obj :type 'rpc-response :json json-obj))
      (mapcar #'build-result json-obj)))
  