aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Sierra <mail@stuartsierra.com>2010-08-21 13:47:12 -0400
committerStuart Sierra <mail@stuartsierra.com>2010-09-17 12:52:44 -0400
commit5a928e263ab88cb8d224de8585932f936aa30c8f (patch)
tree562903ea1ece6ca3b74589072b0fad369edb0700
parent11852b988be61bbf661ca80ef7a78d17e3ee2325 (diff)
Fix EOF-handling in JSON reader; refs #91
-rw-r--r--modules/json/src/main/clojure/clojure/contrib/json.clj106
-rw-r--r--modules/json/src/test/clojure/clojure/contrib/test_json.clj6
2 files changed, 58 insertions, 54 deletions
diff --git a/modules/json/src/main/clojure/clojure/contrib/json.clj b/modules/json/src/main/clojure/clojure/contrib/json.clj
index f8008f7e..0a07b59c 100644
--- a/modules/json/src/main/clojure/clojure/contrib/json.clj
+++ b/modules/json/src/main/clojure/clojure/contrib/json.clj
@@ -29,9 +29,9 @@
;; Expects to be called with the head of the stream AFTER the
;; opening bracket.
(loop [i (.read stream), result (transient [])]
+ (when (neg? i) (throw (EOFException. "JSON error (end-of-file inside array)")))
(let [c (char i)]
(cond
- (= i -1) (throw (EOFException. "JSON error (end-of-file inside array)"))
(Character/isWhitespace c) (recur (.read stream) result)
(= c \,) (recur (.read stream) result)
(= c \]) (persistent! result)
@@ -43,10 +43,9 @@
;; Expects to be called with the head of the stream AFTER the
;; opening bracket.
(loop [i (.read stream), key nil, result (transient {})]
+ (when (neg? i) (throw (EOFException. "JSON error (end-of-file inside array)")))
(let [c (char i)]
(cond
- (= i -1) (throw (EOFException. "JSON error (end-of-file inside object)"))
-
(Character/isWhitespace c) (recur (.read stream) key result)
(= c \,) (recur (.read stream) nil result)
@@ -100,9 +99,9 @@
;; opening quotation mark.
(let [buffer (StringBuilder.)]
(loop [i (.read stream)]
+ (when (neg? i) (throw (EOFException. "JSON error (end-of-file inside array)")))
(let [c (char i)]
(cond
- (= i -1) (throw (EOFException. "JSON error (end-of-file inside string)"))
(= c \") (str buffer)
(= c \\) (do (.append buffer (read-json-escaped-character stream))
(recur (.read stream)))
@@ -112,56 +111,55 @@
(defn- read-json-reader
([^PushbackReader stream keywordize? eof-error? eof-value]
(loop [i (.read stream)]
- (let [c (char i)]
- (cond
- ;; Handle end-of-stream
- (= i -1) (if eof-error?
- (throw (EOFException. "JSON error (end-of-file)"))
- eof-value)
-
- ;; Ignore whitespace
- (Character/isWhitespace c) (recur (.read stream))
-
- ;; Read numbers, true, and false with Clojure reader
- (#{\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9} c)
- (do (.unread stream i)
- (read stream true nil))
-
- ;; Read strings
- (= c \") (read-json-quoted-string stream)
-
- ;; Read null as nil
- (= c \n) (let [ull [(char (.read stream))
- (char (.read stream))
- (char (.read stream))]]
- (if (= ull [\u \l \l])
- nil
- (throw (Exception. (str "JSON error (expected null): " c ull)))))
-
- ;; Read true
- (= c \t) (let [rue [(char (.read stream))
- (char (.read stream))
- (char (.read stream))]]
- (if (= rue [\r \u \e])
- true
- (throw (Exception. (str "JSON error (expected true): " c rue)))))
-
- ;; Read false
- (= c \f) (let [alse [(char (.read stream))
- (char (.read stream))
- (char (.read stream))
- (char (.read stream))]]
- (if (= alse [\a \l \s \e])
- false
- (throw (Exception. (str "JSON error (expected false): " c alse)))))
-
- ;; Read JSON objects
- (= c \{) (read-json-object stream keywordize?)
-
- ;; Read JSON arrays
- (= c \[) (read-json-array stream keywordize?)
-
- :else (throw (Exception. (str "JSON error (unexpected character): " c))))))))
+ (if (neg? i) ;; Handle end-of-stream
+ (if eof-error?
+ (throw (EOFException. "JSON error (end-of-file)"))
+ eof-value)
+ (let [c (char i)]
+ (cond
+ ;; Ignore whitespace
+ (Character/isWhitespace c) (recur (.read stream))
+
+ ;; Read numbers, true, and false with Clojure reader
+ (#{\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9} c)
+ (do (.unread stream i)
+ (read stream true nil))
+
+ ;; Read strings
+ (= c \") (read-json-quoted-string stream)
+
+ ;; Read null as nil
+ (= c \n) (let [ull [(char (.read stream))
+ (char (.read stream))
+ (char (.read stream))]]
+ (if (= ull [\u \l \l])
+ nil
+ (throw (Exception. (str "JSON error (expected null): " c ull)))))
+
+ ;; Read true
+ (= c \t) (let [rue [(char (.read stream))
+ (char (.read stream))
+ (char (.read stream))]]
+ (if (= rue [\r \u \e])
+ true
+ (throw (Exception. (str "JSON error (expected true): " c rue)))))
+
+ ;; Read false
+ (= c \f) (let [alse [(char (.read stream))
+ (char (.read stream))
+ (char (.read stream))
+ (char (.read stream))]]
+ (if (= alse [\a \l \s \e])
+ false
+ (throw (Exception. (str "JSON error (expected false): " c alse)))))
+
+ ;; Read JSON objects
+ (= c \{) (read-json-object stream keywordize?)
+
+ ;; Read JSON arrays
+ (= c \[) (read-json-array stream keywordize?)
+
+ :else (throw (Exception. (str "JSON error (unexpected character): " c)))))))))
(defprotocol Read-JSON-From
(read-json-from [input keywordize? eof-error? eof-value]
diff --git a/modules/json/src/test/clojure/clojure/contrib/test_json.clj b/modules/json/src/test/clojure/clojure/contrib/test_json.clj
index e62df3a8..c38dbc6a 100644
--- a/modules/json/src/test/clojure/clojure/contrib/test_json.clj
+++ b/modules/json/src/test/clojure/clojure/contrib/test_json.clj
@@ -179,6 +179,12 @@
(deftest characters-in-symbols-are-escaped
(is (= "\"foo\\u1b1b\"" (json-str (symbol "foo\u1b1b")))))
+(deftest default-throws-on-eof
+ (is (thrown? java.io.EOFException (read-json ""))))
+
+(deftest can-accept-eof
+ (is (= ::eof (read-json "" true false ::eof))))
+
;;; Pretty-printer
(deftest pretty-printing