annotate src/clojure/contrib/test_contrib/pprint/examples/json.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
rlm@10 1 ;;; json.clj: A pretty printing version of the JavaScript Object Notation (JSON) generator
rlm@10 2
rlm@10 3 ;; by Tom Faulhaber, based on the version by Stuart Sierra (clojure.contrib.json.write)
rlm@10 4 ;; May 9, 2009
rlm@10 5
rlm@10 6 ;; Copyright (c) Tom Faulhaber/Stuart Sierra, 2009. All rights reserved. The use
rlm@10 7 ;; and distribution terms for this software are covered by the Eclipse
rlm@10 8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
rlm@10 9 ;; which can be found in the file epl-v10.html at the root of this
rlm@10 10 ;; distribution. By using this software in any fashion, you are
rlm@10 11 ;; agreeing to be bound by the terms of this license. You must not
rlm@10 12 ;; remove this notice, or any other, from this software.
rlm@10 13
rlm@10 14
rlm@10 15 (ns
rlm@10 16 #^{:author "Tom Faulhaber (based on the version by Stuart Sierra)",
rlm@10 17 :doc "Pretty printing JavaScript Object Notation (JSON) generator.
rlm@10 18
rlm@10 19 This is an example of using a pretty printer dispatch function to generate JSON output",
rlm@10 20 :see-also [["http://json.org/", "JSON Home Page"]]}
rlm@10 21 clojure.contrib.pprint.examples.json
rlm@10 22 (:use [clojure.test :only (deftest- is)]
rlm@10 23 [clojure.contrib.string :only (as-str)]
rlm@10 24 [clojure.contrib.pprint :only (write formatter-out)]))
rlm@10 25
rlm@10 26
rlm@10 27
rlm@10 28 (defmulti dispatch-json
rlm@10 29 "The dispatch function for printing objects as JSON"
rlm@10 30 {:arglists '[[x]]}
rlm@10 31 (fn [x] (cond
rlm@10 32 (nil? x) nil ;; prevent NullPointerException on next line
rlm@10 33 (.isArray (class x)) ::array
rlm@10 34 :else (type x))))
rlm@10 35
rlm@10 36 ;; Primitive types can be printed with Clojure's pr function.
rlm@10 37 (derive java.lang.Boolean ::pr)
rlm@10 38 (derive java.lang.Byte ::pr)
rlm@10 39 (derive java.lang.Short ::pr)
rlm@10 40 (derive java.lang.Integer ::pr)
rlm@10 41 (derive java.lang.Long ::pr)
rlm@10 42 (derive java.lang.Float ::pr)
rlm@10 43 (derive java.lang.Double ::pr)
rlm@10 44
rlm@10 45 ;; Collection types can be printed as JSON objects or arrays.
rlm@10 46 (derive java.util.Map ::object)
rlm@10 47 (derive java.util.Collection ::array)
rlm@10 48
rlm@10 49 ;; Symbols and keywords are converted to strings.
rlm@10 50 (derive clojure.lang.Symbol ::symbol)
rlm@10 51 (derive clojure.lang.Keyword ::symbol)
rlm@10 52
rlm@10 53
rlm@10 54 (defmethod dispatch-json ::pr [x] (pr x))
rlm@10 55
rlm@10 56 (defmethod dispatch-json nil [x] (print "null"))
rlm@10 57
rlm@10 58 (defmethod dispatch-json ::symbol [x] (pr (name x)))
rlm@10 59
rlm@10 60 (defmethod dispatch-json ::array [s]
rlm@10 61 ((formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>") s))
rlm@10 62
rlm@10 63 (defmethod dispatch-json ::object [m]
rlm@10 64 ((formatter-out "~<{~;~@{~<~w:~_~w~:>~^, ~_~}~;}~:>")
rlm@10 65 (for [[k v] m] [(as-str k) v])))
rlm@10 66
rlm@10 67 (defmethod dispatch-json java.lang.CharSequence [s]
rlm@10 68 (print \")
rlm@10 69 (dotimes [i (count s)]
rlm@10 70 (let [cp (Character/codePointAt s i)]
rlm@10 71 (cond
rlm@10 72 ;; Handle printable JSON escapes before ASCII
rlm@10 73 (= cp 34) (print "\\\"")
rlm@10 74 (= cp 92) (print "\\\\")
rlm@10 75 ;; Print simple ASCII characters
rlm@10 76 (< 31 cp 127) (print (.charAt s i))
rlm@10 77 ;; Handle non-printable JSON escapes
rlm@10 78 (= cp 8) (print "\\b")
rlm@10 79 (= cp 12) (print "\\f")
rlm@10 80 (= cp 10) (print "\\n")
rlm@10 81 (= cp 13) (print "\\r")
rlm@10 82 (= cp 9) (print "\\t")
rlm@10 83 ;; Any other character is printed as Hexadecimal escape
rlm@10 84 :else (printf "\\u%04x" cp))))
rlm@10 85 (print \"))
rlm@10 86
rlm@10 87 (defn print-json
rlm@10 88 "Prints x as JSON. Nil becomes JSON null. Keywords become
rlm@10 89 strings, without the leading colon. Maps become JSON objects, all
rlm@10 90 other collection types become JSON arrays. Java arrays become JSON
rlm@10 91 arrays. Unicode characters in strings are escaped as \\uXXXX.
rlm@10 92 Numbers print as with pr."
rlm@10 93 [x]
rlm@10 94 (write x :dispatch dispatch-json))
rlm@10 95
rlm@10 96 (defn json-str
rlm@10 97 "Converts x to a JSON-formatted string."
rlm@10 98 [x]
rlm@10 99 (with-out-str (print-json x)))
rlm@10 100
rlm@10 101
rlm@10 102
rlm@10 103 ;;; TESTS
rlm@10 104
rlm@10 105 ;; Run these tests with
rlm@10 106 ;; (clojure.test/run-tests 'clojure.contrib.print-json)
rlm@10 107
rlm@10 108 ;; Bind clojure.test/*load-tests* to false to omit these
rlm@10 109 ;; tests from production code.
rlm@10 110
rlm@10 111 (deftest- can-print-json-strings
rlm@10 112 (is (= "\"Hello, World!\"" (json-str "Hello, World!")))
rlm@10 113 (is (= "\"\\\"Embedded\\\" Quotes\"" (json-str "\"Embedded\" Quotes"))))
rlm@10 114
rlm@10 115 (deftest- can-print-unicode
rlm@10 116 (is (= "\"\\u1234\\u4567\"" (json-str "\u1234\u4567"))))
rlm@10 117
rlm@10 118 (deftest- can-print-json-null
rlm@10 119 (is (= "null" (json-str nil))))
rlm@10 120
rlm@10 121 (deftest- can-print-json-arrays
rlm@10 122 (is (= "[1, 2, 3]" (json-str [1 2 3])))
rlm@10 123 (is (= "[1, 2, 3]" (json-str (list 1 2 3))))
rlm@10 124 (is (= "[1, 2, 3]" (json-str (sorted-set 1 2 3))))
rlm@10 125 (is (= "[1, 2, 3]" (json-str (seq [1 2 3])))))
rlm@10 126
rlm@10 127 (deftest- can-print-java-arrays
rlm@10 128 (is (= "[1, 2, 3]" (json-str (into-array [1 2 3])))))
rlm@10 129
rlm@10 130 (deftest- can-print-empty-arrays
rlm@10 131 (is (= "[]" (json-str [])))
rlm@10 132 (is (= "[]" (json-str (list))))
rlm@10 133 (is (= "[]" (json-str #{}))))
rlm@10 134
rlm@10 135 (deftest- can-print-json-objects
rlm@10 136 (is (= "{\"a\":1, \"b\":2}" (json-str (sorted-map :a 1 :b 2)))))
rlm@10 137
rlm@10 138 (deftest- object-keys-must-be-strings
rlm@10 139 (is (= "{\"1\":1, \"2\":2}" (json-str (sorted-map 1 1 2 2)))))
rlm@10 140
rlm@10 141 (deftest- can-print-empty-objects
rlm@10 142 (is (= "{}" (json-str {}))))