jsonclojurejsonschemacheshire

Parsing to json with Cheshire - generate- string and parse-string


I am trying to export json-schema to be used outside of Clojure in javascript. I am able to generate this:

{:type "object",
 :properties {:$class {:type "string"},
              :name {:type "string"},
              :clauseId {:type "string"},
              :$identifier {:type "string"}},
 :required [:$class :name :clauseId :$identifier]}

Which is fine for Clojure

With Cheshire using generate-string I can get to:

"{
   \"type\" : \"object\",
   \"properties\" : {
     \"$class\" : {
       \"type\" : \"string\"
     },
     \"name\" : {
       \"type\" : \"string\"
     },
     \"clauseId\" : {
       \"type\" : \"string\"
     },
     \"$identifier\" : {
       \"type\" : \"string\"
     }
   },
   \"required\" : [ \"$class\", \"name\", \"clauseId\", \"$identifier\" ]
 }"

Which is basically what I want but without the quotes. I tried parse-string from Cheshire on the above and I get:

{"type" "object",
 "properties" {"$class" {"type" "string"},
               "name" {"type" "string"},
               "clauseId" {"type" "string"},
               "$identifier" {"type" "string"}},
 "required" ["$class" "name" "clauseId" "$identifier"]}

Closer but it has the colons stripped out. I want

{"type" : "object",
 "properties" : {"$class" : {"type" "string"},
               "name" : {"type" "string"},
               "clauseId" : {"type" "string"},
               "$identifier" : {"type" "string"}},
 "required" : ["$class" "name" "clauseId" "$identifier"]}

I feel like this should be easy and I am missing something. I don't see how it is valid json without the :

How do I create the json with colons?


Solution

  • I think the printing commands are the source of confusion here. You did not show that in your question.

    Another point of confusion is the printing of JSON as "source" or "data". In the latter, a JSON string must have escaped double-quotes. In JS source code, these are not present.

    An example:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test)
      (:require [tupelo.string :as str]))
    
    (dotest
      (let [edn-data {:a 1 :b [2 "three"]}
            json-str (edn->json edn-data)]
    
        (println :a edn-data) ; prints w/o double-quotes
        (prn     :b edn-data) ; prints with double-quotes, as if source code
    
        (newline)
        (println :c json-str)
        (prn     :d json-str)
    

    with result

    -----------------------------------
       Clojure 1.10.3    Java 15.0.2
    -----------------------------------
    
    Testing tst.demo.core
    :a {:a 1, :b [2 three]}
    :b {:a 1, :b [2 "three"]}
    
    :c {"a":1,"b":[2,"three"]}
    :d "{\"a\":1,\"b\":[2,\"three\"]}"
    

    The function edn->json is from the Tupelo library, but is similar to the Cheshire lib et al.

    Please also see this list of Clojure documentation sources, esp. the Clojure CheatSheet.


    If we continue, we can see some nice tricks for dealing with JSON data:

        (let [json-single  (str/quotes->single json-str)
              json-literal "{'a':1,'b':[2,'three']}"
              json-double  (str/quotes->double json-literal)
              edn-data2    (json->edn json-double)]
          (is= json-single json-literal)
          (is= json-double json-str)
          (is= edn-data edn-data2))
        ))
    

    The fundamental problem of having JSON strings in Clojure source code is that it is a string, and needs double-quotes to write as a string literal. This means that the interior double-quotes need to be escaped, which is ugly & error-prone.

    We can avoid this problem is we just write the JSON "literal" using single-quotes, as seen for the variable json-literal. We can then use the function tupelo.string/quotes->double to convert the single quotes into double-quotes inside the string.

    The unit tests show the equivalency of this to the original JSON. Also, since the unit tests sidestep the question of println vs prn, there is no confusion due to the display format on the terminal.


    Oh yeah,

    The above was done in my favorite template project.