commit c22da330f1f5b64a5789bde2bbb958cd9fdc31fa (HEAD, refs/heads/unquoted-json) Author: Alex Ott Date: Sun Oct 31 15:59:01 2010 +0100 add option to control output of non-ASCII characters output functions now accept :escape-chars option, that control how non-ASCII characters in string will output. When this option has value 'true' (default), then hex sequences are used for these characters, and when 'false', then characters are output as-is Modified modules/json/src/main/clojure/clojure/contrib/json.clj diff --git a/modules/json/src/main/clojure/clojure/contrib/json.clj b/modules/json/src/main/clojure/clojure/contrib/json.clj index 0a07b59..360dea6 100644 --- a/modules/json/src/main/clojure/clojure/contrib/json.clj +++ b/modules/json/src/main/clojure/clojure/contrib/json.clj @@ -112,54 +112,54 @@ ([^PushbackReader stream keywordize? eof-error? eof-value] (loop [i (.read stream)] (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))))))))) + (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] @@ -202,6 +202,8 @@ (write-json [object out] "Print object to PrintWriter out as JSON")) +(def ^:dynamic ^{:private true} *escape-non-ascii-chars* true) + (defn- write-json-string [^CharSequence s ^PrintWriter out] (let [sb (StringBuilder. ^Integer (count s))] (.append sb \") @@ -220,8 +222,10 @@ (= cp 10) (.append sb "\\n") (= cp 13) (.append sb "\\r") (= cp 9) (.append sb "\\t") - ;; Any other character is Hexadecimal-escaped - :else (.append sb (format "\\u%04x" cp))))) + ;; Any other character is either Hexadecimal-escaped, or put as-is, depending on options + :else (.append sb (if *escape-non-ascii-chars* + (format "\\u%04x" cp) + (.charAt s i)))))) (.append sb \") (.print out (str sb)))) @@ -302,17 +306,21 @@ {:write-json write-json-generic}) (defn json-str - "Converts x to a JSON-formatted string." - [x] + "Converts x to a JSON-formatted string. +if :escape-chars option is set to false, then Unicode characters are printed as-is" + [x & {:keys [escape-chars] :or {escape-chars true}}] (let [sw (StringWriter.) out (PrintWriter. sw)] - (write-json x out) + (binding [*escape-non-ascii-chars* escape-chars] + (write-json x out)) (.toString sw))) (defn print-json - "Write JSON-formatted output to *out*" - [x] - (write-json x *out*)) + "Write JSON-formatted output to *out*. +if :escape-chars option is set to false, then Unicode characters are printed as-is" + [x & {:keys [escape-chars] :or {escape-chars true}}] + (binding [*escape-non-ascii-chars* escape-chars] + (write-json x *out*))) ;;; JSON PRETTY-PRINTER @@ -339,6 +347,8 @@ :else (pprint-json-generic x))) (defn pprint-json - "Pretty-prints JSON representation of x to *out*" - [x] - (write x :dispatch pprint-json-dispatch)) + "Pretty-prints JSON representation of x to *out*. +if :escape-chars option is set to false, then Unicode characters are printed as-is" + [x & {:keys [escape-chars] :or {escape-chars true}}] + (binding [*escape-non-ascii-chars* escape-chars] + (write x :dispatch pprint-json-dispatch))) Modified modules/json/src/test/clojure/clojure/contrib/test_json.clj 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 c38dbc6..bd19b70 100644 --- a/modules/json/src/test/clojure/clojure/contrib/test_json.clj +++ b/modules/json/src/test/clojure/clojure/contrib/test_json.clj @@ -142,7 +142,9 @@ (is (= "\"\\\"Embedded\\\" Quotes\"" (json-str "\"Embedded\" Quotes")))) (deftest can-print-unicode - (is (= "\"\\u1234\\u4567\"" (json-str "\u1234\u4567")))) + (is (= "\"\\u1234\\u4567\"" (json-str "\u1234\u4567"))) + ;; can print unescaped unicode chars + (is (= "\"\u0442\u0435\u0441\u0442\"" (json-str "\u0442\u0435\u0441\u0442" :escape-chars false)))) (deftest can-print-json-null (is (= "null" (json-str nil))))