skip to Main Content

I’m using json/write-str to serialize a nested JSON object to string.

Example data:

{ :c 1 :a { :d 0 :c 1 :e { :a 0 :1 4 }}}
; json/write-str
; output
{"c":1,"a":{"d":0,"c":1,"e":{"a":0,"1":4}}}

Expected output

{"a":{"c":1,"d":0,"e":{"1":4,"a":0}},"c":1}

This can be trivially solved in JavaScript with the second parameter into the JSON.stringify function, but there doesn’t seem to be an equivalent for Clojure.

2

Answers


  1. The problem is that you have hash maps in your data and the order of elements is undefined there. You have to make sure all the maps in your data are sorted-map and the simplest way do so is to recursively replace all maps by sorted maps:

    
    (require '[clojure.walk :refer [postwalk]])
    
    (def sort-maps
      (partial postwalk (fn [x]
                          (if (map? x)
                            (into (sorted-map) x)
                            x))))
    
    (def data {:c 1 :a {:d 0 :c 1 :e {:a 0 :1 4}}})
    
    (sort-maps data)
    ;; result: {:a {:c 1, :d 0, :e {:1 4, :a 0}}, :c 1}
    
    (json/generate-string (sort-maps data))
    ;; "{"a":{"c":1,"d":0,"e":{"1":4,"a":0}},"c":1}"
    
    
    Login or Signup to reply.
  2. in this exact case you could also use built in :value-fn option of that lib:

    user=> (defn smap [data] (if (map? data) (into (sorted-map) data) data))
    #'user/smap
    
    user=> (json/write-str (smap data) :value-fn #(smap %2))
    "{"a":{"c":1,"d":0,"e":{"1":4,"a":0}},"c":1}"
    
    
    user=> (json/write-str (smap {:x 10 :b {:c 20 :a {:9 100 :3 101}}}) :value-fn #(smap %2))
    "{"b":{"a":{"3":101,"9":100},"c":20},"x":10}"
    

    but notice, that it only works on map values (e.g. it will not sort maps inside an array).

    so the walk solution from another answer is more versatile.

    there are more handy opts applicable in the docs

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search