diff --git a/README.md b/README.md index 4671e3b..9eb7dd5 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ Change Log * Next * Fix [DJSON-50]: `read` can take a PushbackReader for repeated read use case * Fix `write` docstring to add `:indent` option added in [DJSON-18] + * Fix [DJSON-57]: Throw better exception when EOF encountered while reading array or object * Add [DJSON-46]: In `read`, add `:extra-data-fn` that can be provided to cause an eof check after value is read * Add [DJSON-54]: In `write`, add custom fallback fn for writing unknown types * Perf [DJSON-61]: Faster string writing when string is "simple" @@ -246,6 +247,7 @@ Change Log * Source-compatible with clojure.contrib.json, except for the name change. [DJSON-61]: https://clojure.atlassian.net/browse/DJSON-61 +[DJSON-57]: https://clojure.atlassian.net/browse/DJSON-57 [DJSON-54]: https://clojure.atlassian.net/browse/DJSON-54 [DJSON-53]: https://clojure.atlassian.net/browse/DJSON-53 [DJSON-52]: https://clojure.atlassian.net/browse/DJSON-52 diff --git a/src/main/clojure/clojure/data/json.clj b/src/main/clojure/clojure/data/json.clj index aacaaba..e03c579 100644 --- a/src/main/clojure/clojure/data/json.clj +++ b/src/main/clojure/clojure/data/json.clj @@ -325,6 +325,9 @@ (defn invalid-array-exception [] (Exception. "JSON error (invalid array)")) +(defn- eof-array-exception [] + (EOFException. "JSON error (EOF in array)")) + (defn- read-array* [^InternalPBR stream options] ;; Handles all array values after the first. (loop [result (transient [])] @@ -332,6 +335,7 @@ (codepoint-case (int (next-token stream)) \] (persistent! r) \, (recur r) + -1 (throw (eof-array-exception)) (throw (invalid-array-exception)))))) (defn- read-array [^InternalPBR stream options] @@ -342,19 +346,35 @@ (codepoint-case c \] [] \, (throw (invalid-array-exception)) + -1 (throw (eof-array-exception)) (do (.unreadChar stream c) (read-array* stream options))))) +(defn- object-colon-exception [] + (Exception. "JSON error (missing `:` in object)")) + +(defn- eof-object-exception [] + (EOFException. "JSON error (EOF in object)")) + +(defn- invalid-key-exception [c] + (if (= c -1) + (throw (eof-object-exception)) + (throw (Exception. (str "JSON error (non-string key in object), found `" (char c) "`, expected `\"`"))))) + +(comment + (compile 'clojure.data.json) + ) + (defn- read-key [^InternalPBR stream] (let [c (int (next-token stream))] (if (= c (codepoint \")) (let [key (read-quoted-string stream)] (if (= (codepoint \:) (int (next-token stream))) key - (throw (Exception. "JSON error (missing `:` in object)")))) + (throw (object-colon-exception)))) (if (= c (codepoint \})) nil - (throw (Exception. (str "JSON error (non-string key in object), found `" (char c) "`, expected `\"`"))))))) + (invalid-key-exception c))))) (defn- read-object [^InternalPBR stream options] ;; Expects to be called with the head of the stream AFTER the @@ -374,6 +394,7 @@ (codepoint-case (int (next-token stream)) \, (recur r) \} (persistent! r) + -1 (throw (eof-object-exception)) (throw (Exception. "JSON error (missing entry in object)")))) (let [r (persistent! result)] (if (empty? r) diff --git a/src/test/clojure/clojure/data/json_test.clj b/src/test/clojure/clojure/data/json_test.clj index 8eb0354..7a82475 100644 --- a/src/test/clojure/clojure/data/json_test.clj +++ b/src/test/clojure/clojure/data/json_test.clj @@ -407,6 +407,18 @@ (is (thrown? java.io.EOFException (json/read-str "\"\\")))) +(deftest throws-eof-in-arrays + (is (thrown? java.io.EOFException + (json/read-str "[1,"))) + (is (thrown? java.io.EOFException + (json/read-str "[1,2,")))) + +(deftest throws-eof-in-objects + (is (thrown? java.io.EOFException + (json/read-str "{"))) + (is (thrown? java.io.EOFException + (json/read-str "{\"\":1,")))) + (deftest accept-eof (is (= ::eof (json/read-str "" :eof-error? false :eof-value ::eof))))