diff options
author | Stuart Sierra <mail@stuartsierra.com> | 2010-01-20 15:39:56 -0500 |
---|---|---|
committer | Stuart Sierra <mail@stuartsierra.com> | 2010-01-20 15:39:56 -0500 |
commit | 2ede388a9267d175bfaa7781ee9d57532eb4f20f (patch) | |
tree | bb42002af196405d7e25cc4e30b4c1c9de5c06d5 /src/clojure/contrib/json | |
parent | 1bc820d96048a6536706ff999e9892649b53c700 (diff) |
Move source files into Maven-style directory structure.
Diffstat (limited to 'src/clojure/contrib/json')
-rw-r--r-- | src/clojure/contrib/json/read.clj | 338 | ||||
-rw-r--r-- | src/clojure/contrib/json/write.clj | 193 |
2 files changed, 0 insertions, 531 deletions
diff --git a/src/clojure/contrib/json/read.clj b/src/clojure/contrib/json/read.clj deleted file mode 100644 index e9c7a3f2..00000000 --- a/src/clojure/contrib/json/read.clj +++ /dev/null @@ -1,338 +0,0 @@ -;;; json/read.clj: JavaScript Object Notation (JSON) parser - -;; by Stuart Sierra, http://stuartsierra.com/ -;; February 13, 2009 - -;; Copyright (c) Stuart Sierra, 2009. All rights reserved. The use -;; and distribution terms for this software are covered by the Eclipse -;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this -;; distribution. By using this software in any fashion, you are -;; agreeing to be bound by the terms of this license. You must not -;; remove this notice, or any other, from this software. - - -;; Change Log -;; -;; February 13, 2009: added custom handler for quoted strings, to -;; allow escaped forward backslash characters ("\/") in strings. -;; -;; January 26, 2009: initial version - - -;; For more information on JSON, see http://www.json.org/ -;; -;; This library parses data in JSON format. This is a fairly strict -;; implementation of JSON as described at json.org, not a full-fledged -;; JavaScript parser. JavaScript functions and object constructors -;; are not supported. Object field names must be quoted strings; they -;; may not be bare symbols. - - - -(ns - #^{:author "Stuart Sierra", - :doc "JavaScript Object Notation (JSON) parser - - For more information on JSON, see http://www.json.org/ - - This library parses data in JSON format. This is a fairly strict - implementation of JSON as described at json.org, not a full-fledged - JavaScript parser. JavaScript functions and object constructors - are not supported. Object field names must be quoted strings; they - may not be bare symbols. - - If you want to convert map keys from strings to keywords, use - clojure.contrib.walk/keywordize-keys -", - :see-also [["http://www.json.org", "JSON Home Page"]]} - clojure.contrib.json.read - (:import (java.io PushbackReader StringReader EOFException)) - (:use [clojure.test :only (deftest- is)])) - -(declare read-json) - -(def #^{:doc "If true, JSON object keys will be converted to keywords - instead of strings. Defaults to false. There are no checks that - the strings form valid keywords."} *json-keyword-keys* false) - -(defn- read-json-array [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening bracket. - (loop [i (.read stream), result []] - (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 \]) result - :else (do (.unread stream (int c)) - (let [element (read-json stream)] - (recur (.read stream) (conj result element)))))))) - -(defn- read-json-object [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening bracket. - (loop [i (.read stream), key nil, result {}] - (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) - - (= c \:) (recur (.read stream) key result) - - (= c \}) (if (nil? key) - result - (throw (Exception. "JSON error (key missing value in object)"))) - - :else (do (.unread stream i) - (let [element (read-json stream)] - (if (nil? key) - (if (string? element) - (recur (.read stream) element result) - (throw (Exception. "JSON error (non-string key in object)"))) - (recur (.read stream) nil - (assoc result (if *json-keyword-keys* (keyword key) key) - element))))))))) - -(defn- read-json-hex-character [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; initial "\u". Reads the next four characters from the stream. - (let [digits [(.read stream) - (.read stream) - (.read stream) - (.read stream)]] - (when (some neg? digits) - (throw (EOFException. "JSON error (end-of-file inside Unicode character escape)"))) - (let [chars (map char digits)] - (when-not (every? #{\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \a \b \c \d \e \f \A \B \C \D \E \F} - chars) - (throw (Exception. "JSON error (invalid hex character in Unicode character escape)"))) - (char (Integer/parseInt (apply str chars) 16))))) - -(defn- read-json-escaped-character [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; initial backslash. - (let [c (char (.read stream))] - (cond - (#{\" \\ \/} c) c - (= c \b) \backspace - (= c \f) \formfeed - (= c \n) \newline - (= c \r) \return - (= c \t) \tab - (= c \u) (read-json-hex-character stream)))) - -(defn- read-json-quoted-string [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening quotation mark. - (let [buffer (StringBuilder.)] - (loop [i (.read stream)] - (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))) - :else (do (.append buffer c) - (recur (.read stream)))))))) - -(defn read-json - "Read one JSON record from s, which may be a String or a - java.io.PushbackReader." - ([] (read-json *in* true nil)) - ([s] (if (string? s) - (read-json (PushbackReader. (StringReader. s)) true nil) - (read-json s true nil))) - ([#^PushbackReader stream 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) - - ;; Read JSON arrays - (= c \[) (read-json-array stream) - - :else (throw (Exception. (str "JSON error (unexpected character): " c)))))))) - - -(defn read-json-string [string] - (read-json (PushbackReader. (StringReader. string)))) - - -;;; TESTS - -(deftest- can-read-numbers - (is (= 42 (read-json "42"))) - (is (= -3 (read-json "-3"))) - (is (= 3.14159 (read-json "3.14159"))) - (is (= 6.022e23 (read-json "6.022e23")))) - -(deftest- can-read-null - (is (= nil (read-json "null")))) - -(deftest- can-read-strings - (is (= "Hello, World!" (read-json "\"Hello, World!\"")))) - -(deftest- handles-escaped-slashes-in-strings - (is (= "/foo/bar" (read-json "\"\\/foo\\/bar\"")))) - -(deftest- handles-unicode-escapes - (is (= " \u0beb " (read-json "\" \\u0bEb \"")))) - -(deftest- handles-escaped-whitespace - (is (= "foo\nbar" (read-json "\"foo\\nbar\""))) - (is (= "foo\rbar" (read-json "\"foo\\rbar\""))) - (is (= "foo\tbar" (read-json "\"foo\\tbar\"")))) - -(deftest- can-read-booleans - (is (= true (read-json "true"))) - (is (= false (read-json "false")))) - -(deftest- can-ignore-whitespace - (is (= nil (read-json "\r\n null")))) - -(deftest- can-read-arrays - (is (= [1 2 3] (read-json "[1,2,3]"))) - (is (= ["Ole" "Lena"] (read-json "[\"Ole\", \r\n \"Lena\"]")))) - -(deftest- can-read-objects - (is (= {"a" 1, "b" 2} (read-json "{\"a\": 1, \"b\": 2}")))) - -(deftest- can-read-nested-structures - (is (= {"a" [1 2 {"b" [3 "four"]} 5.5]} - (read-json "{\"a\":[1,2,{\"b\":[3,\"four\"]},5.5]}")))) - -(deftest- disallows-non-string-keys - (is (thrown? Exception (read-json "{26:\"z\"")))) - -(deftest- disallows-barewords - (is (thrown? Exception (read-json " foo ")))) - -(deftest- disallows-unclosed-arrays - (is (thrown? Exception (read-json "[1, 2, ")))) - -(deftest- disallows-unclosed-objects - (is (thrown? Exception (read-json "{\"a\":1, ")))) - -(deftest- can-get-keyword-keys - (is (= {:a [1 2 {:b [3 "four"]} 5.5]} - (binding [*json-keyword-keys* true] - (read-json "{\"a\":[1,2,{\"b\":[3,\"four\"]},5.5]}"))))) - -(declare *pass1-string*) - -(deftest- pass1-test - (let [input (read-json *pass1-string*)] - (is (= "JSON Test Pattern pass1" (first input))) - (is (= "array with 1 element" (get-in input [1 "object with 1 member" 0]))) - (is (= 1234567890 (get-in input [8 "integer"]))) - (is (= "rosebud" (last input))))) - -; from http://www.json.org/JSON_checker/test/pass1.json -(def *pass1-string* - "[ - \"JSON Test Pattern pass1\", - {\"object with 1 member\":[\"array with 1 element\"]}, - {}, - [], - -42, - true, - false, - null, - { - \"integer\": 1234567890, - \"real\": -9876.543210, - \"e\": 0.123456789e-12, - \"E\": 1.234567890E+34, - \"\": 23456789012E66, - \"zero\": 0, - \"one\": 1, - \"space\": \" \", - \"quote\": \"\\\"\", - \"backslash\": \"\\\\\", - \"controls\": \"\\b\\f\\n\\r\\t\", - \"slash\": \"/ & \\/\", - \"alpha\": \"abcdefghijklmnopqrstuvwyz\", - \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", - \"digit\": \"0123456789\", - \"0123456789\": \"digit\", - \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", - \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", - \"true\": true, - \"false\": false, - \"null\": null, - \"array\":[ ], - \"object\":{ }, - \"address\": \"50 St. James Street\", - \"url\": \"http://www.JSON.org/\", - \"comment\": \"// /* <!-- --\", - \"# -- --> */\": \" \", - \" s p a c e d \" :[1,2 , 3 - -, - -4 , 5 , 6 ,7 ],\"compact\":[1,2,3,4,5,6,7], - \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", - \"quotes\": \"" \\u0022 %22 0x22 034 "\", - \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" -: \"A key can be any string\" - }, - 0.5 ,98.6 -, -99.44 -, - -1066, -1e1, -0.1e1, -1e-1, -1e00,2e+00,2e-00 -,\"rosebud\"]")
\ No newline at end of file diff --git a/src/clojure/contrib/json/write.clj b/src/clojure/contrib/json/write.clj deleted file mode 100644 index 5f56f211..00000000 --- a/src/clojure/contrib/json/write.clj +++ /dev/null @@ -1,193 +0,0 @@ -;;; json/write.clj: JavaScript Object Notation (JSON) generator - -;; by Stuart Sierra, http://stuartsierra.com/ -;; May 9, 2009 - -;; Copyright (c) Stuart Sierra, 2009. All rights reserved. The use -;; and distribution terms for this software are covered by the Eclipse -;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this -;; distribution. By using this software in any fashion, you are -;; agreeing to be bound by the terms of this license. You must not -;; remove this notice, or any other, from this software. - - -(ns - #^{:author "Stuart Sierra", - :doc "JavaScript Object Notation (JSON) generator. - -This library will generate JSON from the following types: - * nil - * all primitives (Boolean, Byte, Short, Integer, Long, Float, Double) - * String (actually any CharSequence) - * java.util.Map (including Clojure maps) - * java.util.Collection (including Clojure vectors, lists, and sets) - * Java arrays - -You can extend this library to handle new types by adding methods to -print-json. - -This library does NOT attempt to preserve round-trip equality between -JSON and Clojure data types. That is, if you write a JSON string with -this library, then read it back with clojure.contrib.json.read, you -won't necessarily get the exact same data structure. For example, -Clojure sets are written as JSON arrays, which will be read back as -Clojure vectors. - -If you want indented output, try the clojure-json library at -http://github.com/danlarkin/clojure-json - -This implementation attempts to follow the description of JSON at -<http://json.org/>. Maps become JSON objects, all other collections -become JSON arrays. JSON object keys are always converted to strings. -Within strings, all non-ASCII characters are hexadecimal escaped. -", - :see-also [["http://json.org/", "JSON Home Page"]]} - clojure.contrib.json.write - (:require [clojure.contrib.java-utils :as j]) - (:use [clojure.test :only (deftest- is)])) - -(defmulti - #^{:doc "Prints x as JSON. Nil becomes JSON null. Keywords become - strings, without the leading colon. Maps become JSON objects, all - other collection types become JSON arrays. Java arrays become JSON - arrays. Unicode characters in strings are escaped as \\uXXXX. - Numbers print as with pr." - :arglists '([x])} - print-json (fn [x] (cond - (nil? x) nil ;; prevent NullPointerException on next line - (.isArray (class x)) ::array - :else (type x)))) - - -;; Primitive types can be printed with Clojure's pr function. -(derive java.lang.Boolean ::pr) -(derive java.lang.Byte ::pr) -(derive java.lang.Short ::pr) -(derive java.lang.Integer ::pr) -(derive java.lang.Long ::pr) -(derive java.lang.Float ::pr) -(derive java.lang.Double ::pr) - -;; Collection types can be printed as JSON objects or arrays. -(derive java.util.Map ::object) -(derive java.util.Collection ::array) - -;; Symbols and keywords are converted to strings. -(derive clojure.lang.Symbol ::symbol) -(derive clojure.lang.Keyword ::symbol) - - -(defmethod print-json ::pr [x] (pr x)) - -(defmethod print-json nil [x] (print "null")) - -(defmethod print-json ::symbol [x] (print-json (name x))) - -(defmethod print-json ::array [s] - (print \[) - (loop [x s] - (when (seq x) - (let [fst (first x) - nxt (next x)] - (print-json fst) - (when (seq nxt) - (print \,) - (recur nxt))))) - (print \])) - -(defmethod print-json ::object [m] - (print \{) - (loop [x m] - (when (seq m) - (let [[k v] (first x)] - (when (nil? k) - (throw (Exception. "JSON object keys cannot be nil/null"))) - (print-json (j/as-str k)) - (print \:) - (print-json v)) - (let [nxt (next x)] - (when (seq nxt) - (print \,) - (recur nxt))))) - (print \})) - -(defmethod print-json java.lang.CharSequence [#^CharSequence s] - (let [sb (StringBuilder. (count s))] - (.append sb \") - (dotimes [i (count s)] - (let [cp (Character/codePointAt s i)] - (cond - ;; Handle printable JSON escapes before ASCII - (= cp 34) (.append sb "\\\"") - (= cp 92) (.append sb "\\\\") - (= cp 47) (.append sb "\\/") - ;; Print simple ASCII characters - (< 31 cp 127) (.append sb (.charAt s i)) - ;; Handle non-printable JSON escapes - (= cp 8) (.append sb "\\b") - (= cp 12) (.append sb "\\f") - (= 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))))) - (.append sb \") - (print (str sb)))) - -(defn json-str - "Converts x to a JSON-formatted string." - [x] - (with-out-str (print-json x))) - - - -;;; TESTS - -;; Run these tests with -;; (clojure.test/run-tests 'clojure.contrib.print-json) - -;; Bind clojure.test/*load-tests* to false to omit these -;; tests from production code. - -(deftest- can-print-json-strings - (is (= "\"Hello, World!\"" (json-str "Hello, World!"))) - (is (= "\"\\\"Embedded\\\" Quotes\"" (json-str "\"Embedded\" Quotes")))) - -(deftest- can-print-unicode - (is (= "\"\\u1234\\u4567\"" (json-str "\u1234\u4567")))) - -(deftest- can-print-json-null - (is (= "null" (json-str nil)))) - -(deftest- can-print-json-arrays - (is (= "[1,2,3]" (json-str [1 2 3]))) - (is (= "[1,2,3]" (json-str (list 1 2 3)))) - (is (= "[1,2,3]" (json-str (sorted-set 1 2 3)))) - (is (= "[1,2,3]" (json-str (seq [1 2 3]))))) - -(deftest- can-print-java-arrays - (is (= "[1,2,3]" (json-str (into-array [1 2 3]))))) - -(deftest- can-print-empty-arrays - (is (= "[]" (json-str []))) - (is (= "[]" (json-str (list)))) - (is (= "[]" (json-str #{})))) - -(deftest- can-print-json-objects - (is (= "{\"a\":1,\"b\":2}" (json-str (sorted-map :a 1 :b 2))))) - -(deftest- object-keys-must-be-strings - (is (= "{\"1\":1,\"2\":2") (json-str (sorted-map 1 1 2 2)))) - -(deftest- can-print-empty-objects - (is (= "{}" (json-str {})))) - -(deftest- accept-sequence-of-nils - (is (= "[null,null,null]" (json-str [nil nil nil])))) - -(deftest- error-on-nil-keys - (is (thrown? Exception (json-str {nil 1})))) - -(deftest- characters-in-symbols-are-escaped - (is (= "\"foo\\u1b1b\"" (json-str (symbol "foo\u1b1b")))))
\ No newline at end of file |