==========================
== Zhuo Hong Wei's Blog ==
==========================
Any and everything

JSON Helper

I wrote a helper get-json-value to extract values from json output produced by cl-json.


(defun property-p (property)
    (funcall #'consp property))

(defun get-property-value (property)
    (funcall #'cdr property))

(defun get-property-key (property)
    (funcall #'car property))

(defun json-object-p (json)
    (and (listp json) (every #'consp json)))

(defun json-array-p (json)
    (and (listp json) (every json-object-p json)))

(defun json-p (json)
    (or (json-object-p json) (json-array-p json)))

(defun get-json-value (keys json)
    (princ (list keys json))
    (terpri)
    (cond 
        ((null keys) json)
        ((json-p json)
            (let ((key (car keys)))
                (if (numberp key)
                    (get-json-value (cdr keys) (nth key json))
                    (let ((property 
                            (find-if 
                                #'(lambda (property) 
                                        (and (property-p property) 
                                            (eq (get-property-key property) (car keys)))) json)))
                        (if property
                            (get-json-value (cdr keys) (get-property-value property)))))))
        (t nil)))

For example, a json object produced by cl-json looks something like this:

((NUMBER . 7) 
 (MESSAGE . success)
  (PEOPLE 
   ((NAME . Mark Vande Hei) (CRAFT . ISS))
   ((NAME . Oleg Novitskiy) (CRAFT . ISS))
   ((NAME . Pyotr Dubrov) (CRAFT . ISS))
   ((NAME . Thomas Pesquet) (CRAFT . ISS))
   ((NAME . Megan McArthur) (CRAFT . ISS))
   ((NAME . Shane Kimbrough) (CRAFT . ISS))
   ((NAME . Akihiko Hoshide) (CRAFT . ISS))))

To extract the list of people, we supply the path (:people):

(get-json-value `(:people) json)

To extract the first person in the list of people, we extend the path to include an index:

(get-json-value `(:people 0) json)

To get the name of the first person in the list of people, we can further extend the path:

(get-json-value `(:people 0 :name) json)