Welcome! Please see the About page for a little more info on how this works.

0 votes
in data.json by
retagged by

Most EOFs produce helpful error messages like JSON error (end-of-file inside string). But the object and array readers don't handle EOF and throw unhelpful exceptions.

(clojure.data.json/read-str "{") throws Value out of range for char: -1 due to this line in read-key:
(throw (Exception. (str "JSON error (non-string key in object), found " (char c) ", expected \"")))
The (char c) throws because c is -1.

This is fixed by handling the -1 case in read-key:

(defn- read-key [^PushbackReader stream]
  (let [c (int (next-token stream))]
    (if (= c (codepoint \"))
      (let [key (read-quoted-string stream)]
        (if (= (codepoint \:) (int (next-token stream)))
          (throw (Exception. "JSON error (missing `:` in object)"))))
      (codepoint-case c
        \} nil
        -1 (throw (Exception. "JSON error (end-of-file inside object)"))
        (throw (Exception. (str "JSON error (non-string key in object), found `" (char c) "`, expected `\"`")))))))

(clojure.data.json/read-str "{\"\":\"\"") gives an incorrect error message: JSON error (missing entry in object)

This is fixed by handling EOF in read-object:

  (codepoint-case (int (next-token stream))
    \, (recur r)
    \} (persistent! r)
    -1 (throw (Exception. "JSON error (end-of-file inside object)"))
    (throw (Exception. "JSON error (missing entry in object)"))))

(clojure.data.json/read-str "[") throws JSON error (unexpected character): ￿ (the unexpected character is (char 65535)). This is because read-array pushes -1 back onto the stream, and -1 gets pushed as 65535.

The fix is to handle EOF inside read-array:

(defn- read-array [^PushbackReader stream options]
  ;; Expects to be called with the head of the stream AFTER the
  ;; opening bracket.
  ;; Only handles array value.
  (let [c (int (next-token stream))]
    (codepoint-case c
      \] []
      \, (throw (invalid-array-exception))
      -1 (throw (Exception. "JSON error (end-of-file inside array)"))
      (do (.unread stream c)
          (read-array* stream options)))))

2 Answers

0 votes
selected by
Best answer

Logged as https://clojure.atlassian.net/browse/DJSON-57

Note, need to consider perf impacts. It will be preferable to move the exception construction into a separate function to reduce bytecode size and make this more amenable to inlining.

0 votes

Added a patch with a more concise view of the changes.

We work through patch files added to the jira issues. It seems like you have a signed CA already, and you can request a jira account to provide a patch by following the info at https://clojure.org/dev/dev#_becoming_a_contributor to file a contributor support request, and then you can add the patch files directly to the jira.