Search code examples
jsonstringparsingcommon-lisp

How to convert a string representing JSON data to a string on the curl notation in Common Lisp?


I am using Steel Bank Common Lisp (SBCL), Emacs, and Slime.

After executing a function, I have:

CL-USER> (my-function my-data)

"{\"key-1\": \"value-1\"}"

I would like to convert the string "{\"key-1\": \"value-1\"}" to a REST request notation on curl, so a string as: key-1&value-1

Examplifying the application, this is going to be the data on curl:

curl --request POST --data "userId=key-1&value-1" https://jsonplaceholder.typicode.com/posts

Solution

  • The solution is:

    (ql:quickload :yason)
    
    (defun json-string-to-rest-request (json-string)
      (destructuring-bind (a b) (alexandria:hash-table-plist (yason:parse json-string))
        (format nil "~a&~a" a b)))
    

    You can apply it by:

    (defparameter *json-string* "{\"key-1\": \"value-1\"}")
    (defparameter *json-string-1* "{\"KeY-1\": \"value-1\"}")
    
    (json-string-to-rest-request *json-string*)
    ;; => "key-1&value-1"
    (json-string-to-rest-request *json-string-1*)
    ;; => "KeY-1&value-1"
    

    Before this, I tried:

    (ql:quickload :cl-json)
    
    (defun json-string-to-rest (json-string)
      (with-input-from-string (s json-string)
        (destructuring-bind ((a . b)) (cl-json:decode-json s)
          (format nil "~a&~a" (string-downcase a) b))))
    
    (json-string-to-rest *json-string*)
    ;; => "key-1&value-1"
    

    However, the downside is that cl-json:decode-json transforms the json string first to ((:KEY-1 . "value-1")) - so case sensistivity gets lost in the json key string when using cl-json.

    The package yason reads-in the keys as strings and therefore preserves case-sensitivity in the key string.