(ns ird.p2p.impl.json
"JSON on the wire, serialized like JavaScript's JSON.stringify: no
whitespace, object keys in insertion order, integers without a fraction.
`stringify` walks ClojureScript data itself so that a map's own key order
(insertion order for maps up to eight keys) reaches the wire exactly —
the sealed envelope vectors depend on it. `parse` never throws.
Internal interop namespace."
(:require [clojure.string :as str]))
(declare stringify)
(defn- key-name [k]
(cond (keyword? k) (if-let [ns (namespace k)] (str ns "/" (name k)) (name k))
(symbol? k) (str k)
:else (str k)))
(defn stringify
"Compact JSON text of V (maps, vectors/seqs, strings, numbers, booleans,
nil, keywords as their names)."
[v]
(cond
(nil? v) "null"
(string? v) (js/JSON.stringify v)
(keyword? v) (js/JSON.stringify (key-name v))
(number? v) (js/JSON.stringify v)
(boolean? v) (if v "true" "false")
(map? v) (str "{" (str/join "," (map (fn [[k x]] (str (js/JSON.stringify (key-name k)) ":" (stringify x))) v)) "}")
(or (sequential? v) (set? v)) (str "[" (str/join "," (map stringify v)) "]")
:else (js/JSON.stringify (clj->js v))))
(defn parse
"Parse JSON TEXT into ClojureScript data with keyword keys; nil when the
text is not JSON (never throws)."
[text]
(when (string? text)
(try (js->clj (js/JSON.parse text) :keywordize-keys true)
(catch :default _ nil))))
(defn parse-strings
"Like `parse` but keeps object keys as strings."
[text]
(when (string? text)
(try (js->clj (js/JSON.parse text))
(catch :default _ nil))))