jonathan
2020-09-25
High performance JSON encoder and decoder. Currently support: SBCL, CCL.
Jonathan
JSON encoder and decoder.
It's faster than jsown - high performance Common Lisp json parser.
See Document.
This HTML is generated by Codex.
Usage
(to-json '(:name "Common Lisp" :born 1984 :impls (SBCL KCL))) ;; => "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" (to-json '(:name "Common Lisp" :born 1984 :impls (SBCL KCL)) :octets t) ;; => #(123 34 78 65 77 69 34 58 34 67 111 109 109 111 110 32 76 ...) (to-json '((:name . "Common Lisp") (:born . 1984) (:impls SBCL KCL)) :from :alist) ;; => "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" (to-json '(:obj (:name . "Common Lisp") (:born . 1984) (:impls SBCL KCL)) :from :jsown) ;; => "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" (let ((encoder (compile-encoder (:from :alist) (name) `(("name" . ,name))))) (funcall encoder "Rudolph")) ;; => "{\"name\":\"Rudolph\"}" (parse "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}") ;; => (:NAME "Common Lisp" :BORN 1984 :IMPLS ("SBCL" "CCL" "KCL")) (parse "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" :as :alist) ;; => (("NAME" . "Common Lisp") ("BORN" . 1984) ("IMPLS" "SBCL" "CCL" "KCL")) (parse "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" :as :jsown) ;; => (:obj ("NAME" . "Common Lisp") ("BORN" . 1984) ("IMPLS" "SBCL" "CCL" "KCL")) (parse "{\"NAME\":\"Common Lisp\",\"BORN\":1984,\"IMPLS\":[\"SBCL\",\"KCL\"]}" :as :hash-table) ;; => #<HASH-TABLE :TEST EQUAL :COUNT 3>
Installasion
(ql:quickload :jonathan)
to-json
- can encode Object into JSON format.
- Restricted Property List. (
:from :plist
) - Association List. (
:from :alist
) - Jsown Object. (
:from :jsown
)
- Restricted Property List. (
- can return not only string but also octets.
- can be compiled by compiler-macro.
;; Restricted Property List Samples (to-json '(:name :age :born :impls)) ;; => "{\"NAME\":\"AGE\",\"BORN\":\"IMPLS\"}" ;; not "[\"NAME\",\"AGE\",\"BORN\",\"IMPLS\"]" (to-json '(:name "Common Lisp" :born)) ;; => "{\"NAME\":\"Common Lisp\",\"BORN\":[]}"
- is customizable by
%to-json
,%write-char
and%write-string
.
(defclass user () ((id :type integer :initarg :id) (name :type string :initarg :name))) (defmethod %to-json ((user user)) (with-object (write-key-value "id" (slot-value user 'id)) (write-key-value "name" (slot-value user 'name)))) (to-json (make-instance 'user :id 1 :name "Rudolph")) ;; => "{\"id\":1,\"name\":\"Rudolph\"}"
(let ((post (compile-encoder () (text) (list :|channel| "lisp-alien" :|username| "alien-bot" :|text| text :|icon_url| "http://www.lisperati.com/lisplogo_warning2_256.png")))) (time (dotimes (_ 100000) (funcall post "Post from Alien!")))) ;; => 0.095 (flet ((post (text) (jonathan:to-json (list :|channel| "lisp-alien" :|username| "alien-bot" :|text| text :|icon_url| "http://www.lisperati.com/lisplogo_warning2_256.png")))) (time (dotimes (_ 100000) (post "Post from Alien!")))) ;; => 0.095 (flet ((post (text) (format nil "{\"channel\":\"lisp-alien\",\"username\":\"alien-bot\",\"text\":~s,\"icon_url\":\"http://www.lisperati.com/lisplogo_warning2_256.png\"}" text))) (time (dotimes (_ 100000) (post "Post from Alien!"))))"\"}")))) ;; => 0.146 (flet ((post (text) (jonathan:to-json (list :|channel| "lisp-alien" :|username| "alien-bot" :|text| text :|icon_url| "http://www.lisperati.com/lisplogo_warning2_256.png")))) (time (dotimes (_ 100000) (post "Post from Alien!")))) ;; => 0.604 - without compiler-macro. (flet ((post (text) (jsown:to-json `(:obj (:|channel| . "lisp-alien") (:|username| . "alien-bot") (:|text| . ,text) (:|icon_url| . "http://www.lisperati.com/lisplogo_warning2_256.png"))))) (time (dotimes (_ 100000) (post "Post from Alien!")))) ;; => 1.117
parse
- can decode JSON format string into Object.
- Property List. (
:as :plist
) - Association List. (
:as :alist
) - Json Object. (
:as :jsown
) - Hash Table. (
:as :hash-table
)
- Property List. (
- can allow junked JSON format string (
:junk-allowed t
) - can customize
*null-value*
,*false-value*
and*empty-array-value*
. - can restrict keywords to read. (
:keywords-to-read
) - can normalize keywords. (
:keyword-normalizer
)- can not normalize keywords in nested objects.
- can ignore keywords when normalizer returns NIL.
- can unescape unicode escape sequences. (
:unescape-unicode-escape-sequence
)
(parse "{\"key\":\"value\"}") ;; => (:|key| "value") (parse "{\"key\":\"value\"") ;; => raise <jonathan-unexpected-eof>. (parse "{\"key\":\"value\"" :junk-allowed t) ;; => (:|key| "value") (let ((*null-value* :null) (*false-value* :false) (*empty-array-value* :[])) (parse "{\"null\":null,\"false\":false,\"empty\":[]}")) ;; => (:|null| :NULL :|false| :FALSE :|empty| :[]) (parse "{\"key1\":\"value1\",\"key2\":\"value2\"}" :keywords-to-read '("key1")) ;; => (:|key1| "value1") (flet ((normalizer (key) (with-vector-parsing (key) (match-i-case ("key1" (return-from normalizer "other-key1")) ("key2" (return-from normalizer "other-key2")) (otherwise (return-from normalizer nil)))))) (parse "{\"KEY1\":{\"key2\":\"value2\"},\"key3\":\"value3\"}" :keyword-normalizer #'normalizer) ;; => (:|other-key1| (:|key2| "value2")) (parse "{\"KEY1\":{\"key2\":\"value2\"},\"key3\":\"value3\"}" :keyword-normalizer #'normalizer :normalize-all t)) ;; => (:|other-key1| (:|other-key2| "value2")) (parse "\"\\u30b8\\u30e7\\u30ca\\u30b5\\u30f3\"") ;; => "ジョナサン" (parse "\"\\uD840\\uDC0B\"") ;; => " B" (parse "\"\\u30b8\\u30e7\\u30ca\\u30b5\\u30f3\"" :unescape-unicode-escape-sequence nil) ;; => "\u30b8\u30e7\u30ca\u30b5\u30f3"
(let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jonathan:parse s :as :alist)))) ;; => 0.174 (let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jonathan:parse s :as :jsown)))) ;; => 0.181 (let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jsown:parse s)))) ;; => 0.204
(let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jonathan:parse s :as :alist :keywords-to-read '("key1"))))) ;; => 0.065 (let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jonathan:parse s :as :jsown :keywords-to-read '("key1"))))) ;; => 0.069 (let ((s "{\"key1\":\"value\",\"key2\":1.1,\"key3\":[\"Hello\",1.2]}")) (time (dotimes (_ 100000) (jsown:parse s "key1")))) ;; => 0.085
Helper
compile-encoder
- can compile encoder.
(compile-encoder () (name) (list :name name)) ;; => #<FUNCTION (LAMBDA (name))> (funcall * "Rudolph") ;; => "{\"NAME\":\"Rudolph\"}" (compile-encoder (:from :alist) (name) `(("name" . ,name))) ;; => #<FUNCTION (LAMBDA (name))> (funcall * "Rudolph") ;; => "{\"name\":\"Rudolph\"}" (compile-encoder (:octets t) (name) (list :name name)) ;; => #<FUNCTION (LAMBDA (name))> (funcall * "Rudolph") ;; => #(123 34 75 69 89 49 ...)
with-object
(defclass user () ((id :initarg :id) (name :initarg :name))) (defmethod %to-json ((user user)) (with-object (write-key "id") (write-value (slot-value user 'id)) (write-key-value "name" (slot-value user 'name)))) (to-json (make-instance 'user :id 1 :name "Rudolph")) ;; => "{\"id\":1,\"name\":\"Rudolph\"}"
with-array
(defclass user () ((id :initarg :id) (name :initarg :name))) (defmethod %to-json ((user user)) (with-array (write-item "id") (write-item (slot-value user 'id)) (write-item "name") (write-item (slot-value user 'name)))) (to-json (make-instance 'user :id 1 :name "Rudolph")) ;; => "[\"id\",1,\"name\",\"Rudolph\"]"
with-output
(with-output-to-string (stream) (with-output (stream) (with-object (write-key-value "key" "value")))) ;; => "{\"key\":\"value\"}"
with-output-to-string*
(with-output-to-string* (with-object (write-key-value "key" "value")))) ;; => "{\"key\":\"value\"}"
See Also
Author
- Rudolph-Miller
Copyright
Copyright (c) 2015 Rudolph-Miller
Dependents (39)
- cl-coinpayments
- cl-coveralls
- cl-covid19
- cl-fluent-logger
- cl-incognia
- cl-jingle
- cl-jwk
- cl-megolm
- cl-rethinkdb
- cl-sendgrid
- cl-tasukete
- cl-telebot
- cl-telegram-bot
- cl-torrents
- cl-transmission
- cl-weather-jp
- datafly
- doc
- http-body
- hunchenissr
- integral-rest
- jose
- latter-day-paypal
- lispcord
- lisp-pay
- log4cl-extras
- lunamech-matrix-api
- oook
- openid-key
- psychiq
- reblocks
- reblocks-auth
- reblocks-websocket
- restful
- rs-json
- slack-client
- stepster
- stripe-against-the-modern-world
- vom-json