Json in Lisp

Our program demonstrates JSON encoding and decoding in Lisp, including working with built-in and custom data types.

(ql:quickload :cl-json)

(defpackage :json-example
  (:use :cl :cl-json))

(in-package :json-example)

;; We'll use these two structs to demonstrate encoding and
;; decoding of custom types below.
(defstruct response1
  page
  fruits)

(defstruct response2
  (page nil :json-key "page")
  (fruits nil :json-key "fruits"))

(defun main ()
  ;; First we'll look at encoding basic data types to
  ;; JSON strings. Here are some examples for atomic values.
  (format t "~a~%" (json:encode-json-to-string t))
  (format t "~a~%" (json:encode-json-to-string 1))
  (format t "~a~%" (json:encode-json-to-string 2.34))
  (format t "~a~%" (json:encode-json-to-string "gopher"))

  ;; And here are some for lists and hash tables, which encode
  ;; to JSON arrays and objects as you'd expect.
  (let ((slc '("apple" "peach" "pear")))
    (format t "~a~%" (json:encode-json-to-string slc)))

  (let ((map (make-hash-table :test 'equal)))
    (setf (gethash "apple" map) 5)
    (setf (gethash "lettuce" map) 7)
    (format t "~a~%" (json:encode-json-to-string map)))

  ;; The JSON package can automatically encode your
  ;; custom data types.
  (let ((res1 (make-response1 :page 1 :fruits '("apple" "peach" "pear"))))
    (format t "~a~%" (json:encode-json-to-string res1)))

  ;; You can use the :json-key option in struct definitions
  ;; to customize the encoded JSON key names.
  (let ((res2 (make-response2 :page 1 :fruits '("apple" "peach" "pear"))))
    (format t "~a~%" (json:encode-json-to-string res2)))

  ;; Now let's look at decoding JSON data into Lisp
  ;; values. Here's an example for a generic data structure.
  (let* ((json-string "{\"num\":6.13,\"strs\":[\"a\",\"b\"]}")
         (decoded (json:decode-json-from-string json-string)))
    (format t "~a~%" decoded)
    (format t "~a~%" (cdr (assoc :num decoded)))
    (format t "~a~%" (car (cdr (assoc :strs decoded)))))

  ;; We can also decode JSON into custom data types.
  (let* ((json-string "{\"page\": 1, \"fruits\": [\"apple\", \"peach\"]}")
         (res (json:decode-json-from-string json-string)))
    (format t "~a~%" res)
    (format t "~a~%" (cdr (assoc :fruits res))))

  ;; In the examples above we always used strings as intermediates
  ;; between the data and JSON representation. We can also
  ;; stream JSON encodings directly to output streams.
  (let ((map (make-hash-table :test 'equal)))
    (setf (gethash "apple" map) 5)
    (setf (gethash "lettuce" map) 7)
    (json:encode-json map *standard-output*)))

(main)

To run the program, save it as json-example.lisp and use your Lisp interpreter. For example, if you’re using SBCL:

$ sbcl --load json-example.lisp

This will output:

true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"PAGE":1,"FRUITS":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
((:NUM . 6.13) (:STRS "a" "b"))
6.13
a
(:PAGE 1 :FRUITS ("apple" "peach"))
("apple" "peach")
{"apple":5,"lettuce":7}

We’ve covered the basics of JSON in Lisp here. The cl-json library provides a comprehensive set of functions for working with JSON in Common Lisp. Remember to check the library’s documentation for more advanced usage and options.