Search code examples
jsonstringescapingcommon-lisppretty-print

How to convert a stringfied JSON from a no-indentation and escape backslashes to a beautifully indented and free of escape characters JSON?


I am using Common Lisp, SBCL, and a famous library called Dexador. The following s-exp:

CL-USER> (dex:post "https://html2json.com/api/v1"
          :content (dex:get "https://ambrevar.xyz"))

Returns:

"{\"error\":false,\"code\":null,\"message\":null,\"data\":{\"head\":{\"title\":\"\\u200e\",\"meta\":[{\"charset\":\"utf-8\"},{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"},{\"name\":\"generator\",\"content\":\"Org mode\"},{\"name\":\"author\",\"content\":\"root\"}],\"link\":[{\"rel\":\"stylesheet\",\"type\":\"text\\/css\",\"href\":\"..\\/dark.css\"},{\"rel\":\"icon\",\"type\":\"image\\/x-icon\",\"href\":\"..\\/logo.png\"}],\"script\":[]},\"headers\":[{\"tagName\":\"h2\",\"textContent\":\"Contact\",\"attributes\":{\"id\":\"org4051da4\"}}],\"jsonLd\":[],\"iframes\":[],\"embeds\":[],\"imgs\":[],\"links\":[{\"text\":\"[email protected]\",\"href\":\"mailto:[email protected]\",\"attr\":{\"href\":\"mailto:[email protected]\"}},{\"text\":\"0x9BDCF497A4BBCC7F\",\"href\":\"ambrevar.asc\",\"attr\":{\"href\":\"ambrevar.asc\"}}],\"forms\":[],\"tables\":[],\"content\":\"Contact\\nEmail: [email protected]\\nPGP: 0x9BDCF497A4BBCC7F\"}}"
200
#<HASH-TABLE :TEST EQUAL :COUNT 17 {10039E4303}>
#<QURI.URI.HTTP:URI-HTTPS https://html2json.com/api/v1>
#<CL+SSL::SSL-STREAM for #<FD-STREAM for "socket 172.20.10.5:34050, peer: 185.94.230.235:443" {10038A5B13}>>

The first result (of a brig stringfied JSON) is close to what I want.

Since the JSON output is going to be printed to end users, I need (i) to remove backslash (\) and (ii) indent JSON.

However, the backslashes exist due to the escape nature they have on quotes (to avoid premature closing of the string) and I am not sure how to handle them. I am also clueless on how to pretty print it.

How can I achieve the desired result? Is there a library to solve this specific problem? If so, how?

Thanks


Solution

  • There is! But it is not quickload-able. You have to download it from github. The most convenient way to set it up however is to use roswell (which can install you github repository maintained Common Lisp packages as well as quickload-able packages).

    I wrote an article once about setting up Roswell for a Development environment of Common Lisp - see: https://towardsdatascience.com/how-to-set-up-common-lisp-ide-in-2021-5be70d88975b .

    If you have then installed Roswell, setting up is as straight forward as:

    $ ros install muyinliu/jsown-utils # installs from github directly
    $ ros install dexador              # installs from quicklisp repo
    

    And you are ready to go!

    (ql:quickload 'jsown-utils)
    (ql:quickload 'dexador)
    
    ;; https://github.com/muyinliu/jsown-utils
    
    (jsown:pprint-json
     (dex:post "https://html2json.com/api/v1"
               :content (dex:get "https://ambrevar.xyz")))
    

    jsown:pprint-json is exactly the function you are searching for!

    The code above outputs:

    {
        "error": [],
        "code": [],
        "message": [],
        "data": {
            "head": {
                "title": "‎",
                "meta": [
                    {
                        "charset": "utf-8"
                    },
                    {
                        "name": "viewport",
                        "content": "width=device-width, initial-scale=1"
                    },
                    {
                        "name": "generator",
                        "content": "Org mode"
                    },
                    {
                        "name": "author",
                        "content": "root"
                    }
                ],
                "link": [
                    {
                        "rel": "stylesheet",
                        "type": "text/css",
                        "href": "../dark.css"
                    },
                    {
                        "rel": "icon",
                        "type": "image/x-icon",
                        "href": "../logo.png"
                    }
                ],
                "script": []
            },
            "headers": [
                {
                    "tagName": "h2",
                    "textContent": "Contact",
                    "attributes": {
                        "id": "org4051da4"
                    }
                }
            ],
            "jsonLd": [],
            "iframes": [],
            "embeds": [],
            "imgs": [],
            "links": [
                {
                    "text": "[email protected]",
                    "href": "mailto:[email protected]",
                    "attr": {
                        "href": "mailto:[email protected]"
                    }
                },
                {
                    "text": "0x9BDCF497A4BBCC7F",
                    "href": "ambrevar.asc",
                    "attr": {
                        "href": "ambrevar.asc"
                    }
                }
            ],
            "forms": [],
            "tables": [],
            "content": "Contact
    Email: [email protected]
    PGP: 0x9BDCF497A4BBCC7F"
        }
    }
    

    without Roswell, you have to follow jsown-util's github Readme instructions:

    # In shell I would do:
    
    cd ~/quicklisp/local-projects # enter your quicklisp/local-projects
    # and then git clone it:
    git clone https://github.com/muyinliu/jsown-utils.git
    
    
    # Then in Common Lisp to install the package using quicklisp:
    
    (ql:quickload 'jsown-utils)
    

    Basically it means to go into your quicklisp's local-projects folder and git clone the github repository of jsown-utils there. Because, then it is visible for ql:quickload which always checks in its local-projects folder first, before it searches for the package in the official quicklisp repo online.