-
Notifications
You must be signed in to change notification settings - Fork 3
/
json.clj
100 lines (87 loc) · 3.44 KB
/
json.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
(ns telegrambot-lib.json
"Internal JSON mapping utilities."
(:gen-class)
(:require [clojure.string :as string]
[clojure.tools.logging :as log]))
(defn- resolve-fn-var
"Returns the var to which a `var-name` will be resolved in the `ns` namespace
(unless found in the environment), else `nil`. Also checks that the returned
var dereferences to a function."
[ns var-name]
(let [resolved-var (ns-resolve ns (symbol var-name))]
(if (and (some? resolved-var) (not (fn? @resolved-var)))
(log/warnf "The resolved var '%s/%s' do not reference a function" ns var-name)
resolved-var)))
(def supported-mapping-libs
"The JSON mapping libraries supported in the current implementation."
[{:core-lib-ns 'cheshire.core
:group-id+name "cheshire"
:parser-fn-name "parse-string"
:parser-builder (fn [parser-fn]
#(parser-fn % true))
:generator-fn-name "generate-string"}
{:core-lib-ns 'jsonista.core
:group-id+name "metosin/jsonista"
:parser-fn-name "read-value"
:parser-builder (fn [parser-fn]
(let [object-mapper ((resolve-fn-var 'jsonista.core
"object-mapper")
{:decode-key-fn true})]
#(parser-fn % object-mapper)))
:generator-fn-name "write-value-as-string"}
{:core-lib-ns 'clojure.data.json
:group-id+name "org.clojure/data.json"
:parser-fn-name "read-str"
:parser-builder (fn [parser-fn]
#(parser-fn % :key-fn keyword))
:generator-fn-name "write-str"}])
(let [lock (Object.)]
(defn- is-lib-present?
"Checks for an optional dependency presence in classpath by resolving
its core ns."
[core-lib-ns]
(locking lock
(try
(require core-lib-ns)
true
(catch Throwable _ false)))))
(def ^:private present-mapping-libs
"A subset of the supported JSON mapping libraries that are currently present
in classpath."
(filter #(is-lib-present? (:core-lib-ns %))
supported-mapping-libs))
(when-not (seq present-mapping-libs)
(throw (IllegalStateException.
(format "No supported JSON mapper library supplied. Consider adding one of the %s to the project deps."
(->> supported-mapping-libs
(map #(str "[\"" (:group-id+name %) "\"]"))
(string/join ", "))))))
(defn- build-json-str-parser
"Builds a JSON string parser fn — the one that takes a JSON-encoded string
and parses a Clojure object out of it."
[lib]
(let [parser-fn (resolve-fn-var (:core-lib-ns lib)
(:parser-fn-name lib))]
((:parser-builder lib) parser-fn)))
(def ^:private json-str-parser
(->> present-mapping-libs
first
build-json-str-parser))
(defn parse-str
"Parses (reads) a given JSON-encoded string into a Clojure object."
[json-str]
(json-str-parser json-str))
(defn- build-json-str-generator
"Builds a JSON string generator fn — the one that takes a Clojure object
and generates its JSON-encoded string representation."
[lib]
(resolve-fn-var (:core-lib-ns lib)
(:generator-fn-name lib)))
(def ^:private json-str-generator
(->> present-mapping-libs
first
build-json-str-generator))
(defn generate-str
"Generates (writes) a JSON-encoded string for a given Clojure object."
[clj-obj]
(json-str-generator clj-obj))