-
Notifications
You must be signed in to change notification settings - Fork 151
/
parse.clj
100 lines (88 loc) · 3.59 KB
/
parse.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 cheshire.parse
(:import (com.fasterxml.jackson.core JsonParser JsonToken)))
(declare parse*)
(def ^:dynamic *chunk-size* 32)
(def ^{:doc "Flag to determine whether float values should be returned as
BigDecimals to retain precision. Defaults to false."
:dynamic true}
*use-bigdecimals?* false)
(defmacro ^:private tag
([obj]
`(vary-meta ~obj assoc :tag `JsonParser)))
(definline parse-object [^JsonParser jp key-fn bd? array-coerce-fn]
(let [jp (tag jp)]
`(do
(.nextToken ~jp)
(loop [mmap# (transient {})]
(if-not (identical? (.getCurrentToken ~jp)
JsonToken/END_OBJECT)
(let [key-str# (.getText ~jp)
_# (.nextToken ~jp)
key# (~key-fn key-str#)
mmap# (assoc! mmap# key#
(parse* ~jp ~key-fn ~bd? ~array-coerce-fn))]
(.nextToken ~jp)
(recur mmap#))
(persistent! mmap#))))))
(definline parse-array [^JsonParser jp key-fn bd? array-coerce-fn]
(let [jp (tag jp)]
`(let [array-field-name# (.getCurrentName ~jp)]
(.nextToken ~jp)
(loop [coll# (transient (if ~array-coerce-fn
(~array-coerce-fn array-field-name#)
[]))]
(if-not (identical? (.getCurrentToken ~jp)
JsonToken/END_ARRAY)
(let [coll# (conj! coll#
(parse* ~jp ~key-fn ~bd? ~array-coerce-fn))]
(.nextToken ~jp)
(recur coll#))
(persistent! coll#))))))
(defn lazily-parse-array [^JsonParser jp key-fn bd? array-coerce-fn]
(lazy-seq
(loop [chunk-idx 0, buf (chunk-buffer *chunk-size*)]
(if (identical? (.getCurrentToken jp) JsonToken/END_ARRAY)
(chunk-cons (chunk buf) nil)
(do
(chunk-append buf (parse* jp key-fn bd? array-coerce-fn))
(.nextToken jp)
(let [chunk-idx* (unchecked-inc chunk-idx)]
(if (< chunk-idx* *chunk-size*)
(recur chunk-idx* buf)
(chunk-cons
(chunk buf)
(lazily-parse-array jp key-fn bd? array-coerce-fn)))))))))
(defn parse* [^JsonParser jp key-fn bd? array-coerce-fn]
(condp identical? (.getCurrentToken jp)
JsonToken/START_OBJECT (parse-object jp key-fn bd? array-coerce-fn)
JsonToken/START_ARRAY (parse-array jp key-fn bd? array-coerce-fn)
JsonToken/VALUE_STRING (.getText jp)
JsonToken/VALUE_NUMBER_INT (.getNumberValue jp)
JsonToken/VALUE_NUMBER_FLOAT (if bd?
(.getDecimalValue jp)
(.getNumberValue jp))
JsonToken/VALUE_EMBEDDED_OBJECT (.getBinaryValue jp)
JsonToken/VALUE_TRUE true
JsonToken/VALUE_FALSE false
JsonToken/VALUE_NULL nil
(throw
(Exception.
(str "Cannot parse " (pr-str (.getCurrentToken jp)))))))
(defn parse-strict [^JsonParser jp key-fn eof array-coerce-fn]
(let [key-fn (or (if (identical? key-fn true) keyword key-fn) identity)]
(.nextToken jp)
(condp identical? (.getCurrentToken jp)
nil
eof
(parse* jp key-fn *use-bigdecimals?* array-coerce-fn))))
(defn parse [^JsonParser jp key-fn eof array-coerce-fn]
(let [key-fn (or (if (and (instance? Boolean key-fn) key-fn) keyword key-fn) identity)]
(.nextToken jp)
(condp identical? (.getCurrentToken jp)
nil
eof
JsonToken/START_ARRAY
(do
(.nextToken jp)
(lazily-parse-array jp key-fn *use-bigdecimals?* array-coerce-fn))
(parse* jp key-fn *use-bigdecimals?* array-coerce-fn))))