Skip to content

Commit

Permalink
Merge pull request #134 from puppetlabs/PE-38477-add-java-time-json-e…
Browse files Browse the repository at this point in the history
…ncoders

(PE-38477) add java.time.* json encoders
  • Loading branch information
jonathannewman authored May 28, 2024
2 parents b1521f9 + 53d1e90 commit 1e371b1
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 31 deletions.
55 changes: 47 additions & 8 deletions src/puppetlabs/kitchensink/json.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,68 @@
This namespace when 'required' will also setup some common JSON encoders
globally, so you can avoid doing this for each call."
(:require [cheshire.core :as core]
[cheshire.generate :as generate]
[clj-time.coerce :as coerce]
[clj-time.core :as clj-time]
[clojure.java.io :as io]
[clojure.tools.logging :as log])
(:import com.fasterxml.jackson.core.JsonGenerator))
(:require
[cheshire.core :as core]
[cheshire.generate :as generate]
[clj-time.coerce :as coerce]
[clj-time.core :as clj-time]
[clojure.java.io :as io]
[clojure.tools.logging :as log])
(:import
com.fasterxml.jackson.core.JsonGenerator
(java.time Instant LocalDate LocalDateTime)))

(defn- clj-time-encoder
[data jsonGenerator]
(.writeString ^JsonGenerator jsonGenerator ^String (coerce/to-string data)))

(def ^:dynamic *datetime-encoder* clj-time-encoder)

(defn- java-instant-encoder
[^Instant data jsonGenerator]
(.writeString ^JsonGenerator jsonGenerator ^String (.toString data)))

(def ^:dynamic *instant-encoder* java-instant-encoder)

(defn- java-localdate-encoder
[^LocalDate data jsonGenerator]
(.writeString ^JsonGenerator jsonGenerator ^String (.toString data)))

(def ^:dynamic *localdate-encoder* java-localdate-encoder)

(defn- java-localdatetime-encoder
[^LocalDateTime data jsonGenerator]
(.writeString ^JsonGenerator jsonGenerator ^String (.toString data)))

(def ^:dynamic *localdatetime-encoder* java-localdatetime-encoder)

(defn add-common-json-encoders!*
"Non-memoize version of add-common-json-encoders!"
[]
(when (satisfies? generate/JSONable (clj-time/date-time 1999))
(log/warn "Overriding existing JSONable protocol implementation for org.joda.time.DateTime"))
(when (satisfies? generate/JSONable (Instant/now))
(log/warn "Overriding existing JSONable protocol implementation for java.time.Instant"))
(when (satisfies? generate/JSONable (LocalDateTime/now))
(log/warn "Overriding existing JSONable protocol implementation for java.time.LocalDateTime"))
(when (satisfies? generate/JSONable (LocalDate/now))
(log/warn "Overriding existing JSONable protocol implementation for java.time.LocalDate"))
(generate/add-encoder
org.joda.time.DateTime
(fn [data jsonGenerator]
(*datetime-encoder* data jsonGenerator))))
(*datetime-encoder* data jsonGenerator)))
(generate/add-encoder
java.time.Instant
(fn [data jsonGenerator]
(*instant-encoder* data jsonGenerator)))
(generate/add-encoder
java.time.LocalDate
(fn [data jsonGenerator]
(*localdate-encoder* data jsonGenerator)))
(generate/add-encoder
java.time.LocalDateTime
(fn [data jsonGenerator]
(*localdatetime-encoder* data jsonGenerator))))

(def
^{:doc "Registers some common encoders for cheshire JSON encoding.
Expand Down
64 changes: 41 additions & 23 deletions test/puppetlabs/kitchensink/json_test.clj
Original file line number Diff line number Diff line change
@@ -1,74 +1,92 @@
(ns puppetlabs.kitchensink.json-test
(:require [clj-time.core :as clj-time]
[puppetlabs.kitchensink.core :refer [temp-file]]
[puppetlabs.kitchensink.json :refer [add-common-json-encoders!*
generate-pretty-stream generate-pretty-string generate-stream
generate-string parse-stream parse-string spit-json
with-datetime-encoder]])
(:require
[clj-time.core :as clj-time]
[puppetlabs.kitchensink.core :refer [temp-file]]
[puppetlabs.kitchensink.json :as ks-json])
(:use [clojure.test])
(:import (com.fasterxml.jackson.core JsonGenerator)
(java.io StringReader StringWriter)))
(:import
(com.fasterxml.jackson.core JsonGenerator)
(java.io StringReader StringWriter)
(java.time Instant LocalDate LocalDateTime)))

(defn add-common-encoders-fixture
[f]
(add-common-json-encoders!*)
(ks-json/add-common-json-encoders!*)
(f))

(use-fixtures :once add-common-encoders-fixture)

(deftest test-with-custom-datetime-encoder
(testing "should allow use of custom encoder"
(is (= (with-datetime-encoder (fn [_dt gn8r] (.writeString ^JsonGenerator gn8r "Beer-o-clock"))
(generate-string (clj-time/date-time 1989 11 17 5 6 24 654)))
(is (= (ks-json/with-datetime-encoder (fn [_dt gn8r] (.writeString ^JsonGenerator gn8r "Beer-o-clock"))
(ks-json/generate-string (clj-time/date-time 1989 11 17 5 6 24 654)))
"\"Beer-o-clock\""))))

(deftest test-generate-string
(testing "should generate a json string"
(is (= (generate-string {:a 1 :b 2})
(is (= (ks-json/generate-string {:a 1 :b 2})
"{\"a\":1,\"b\":2}")))
(testing "should generate a json string that has a Joda DataTime object in it and not explode"
(is (= (generate-string {:a 1 :b (clj-time/date-time 1986 10 14 4 3 27 456)})
"{\"a\":1,\"b\":\"1986-10-14T04:03:27.456Z\"}"))))
(is (= (ks-json/generate-string {:a 1 :b (clj-time/date-time 1986 10 14 4 3 27 456)})
"{\"a\":1,\"b\":\"1986-10-14T04:03:27.456Z\"}")))
(testing "should generate a json string that has a java.time.Instant in it and not explode"
(is (= (ks-json/generate-string {:a 1 :b (Instant/parse "1994-09-19T21:00:30Z")})
"{\"a\":1,\"b\":\"1994-09-19T21:00:30Z\"}")))
(testing "should generate a json string that has a java.time.LocalDate in it and not explode"
(is (= (ks-json/generate-string {:a 1 :b (LocalDate/parse "1953-05-29")})
"{\"a\":1,\"b\":\"1953-05-29\"}")))
(testing "should generate a json string that has a java.time.Instant in it and not explode"
(is (= (ks-json/generate-string {:a 1 :b (LocalDateTime/parse "1954-07-31T21:00:30")})
"{\"a\":1,\"b\":\"1954-07-31T21:00:30\"}"))))

(deftest test-generate-pretty-string
(testing "should generate a json string"
(is (= (generate-pretty-string {:a 1 :b 2})
(is (= (ks-json/generate-pretty-string {:a 1 :b 2})
"{\n \"a\" : 1,\n \"b\" : 2\n}")))
(testing "should generate a json string that has a Joda DataTime object in it and not explode"
(is (= (generate-pretty-string {:a 1 :b (clj-time/date-time 1986 10 14 4 3 27 456)})
"{\n \"a\" : 1,\n \"b\" : \"1986-10-14T04:03:27.456Z\"\n}"))))
(is (= (ks-json/generate-pretty-string {:a 1 :b (clj-time/date-time 1986 10 14 4 3 27 456)})
"{\n \"a\" : 1,\n \"b\" : \"1986-10-14T04:03:27.456Z\"\n}")))
(testing "should generate a json string that has a java.time.Instant in it and not explode"
(is (= (ks-json/generate-pretty-string {:a 1 :b (Instant/parse "1994-09-19T21:00:30Z")})
"{\n \"a\" : 1,\n \"b\" : \"1994-09-19T21:00:30Z\"\n}")))
(testing "should generate a json string that has a java.time.LocalDate in it and not explode"
(is (= (ks-json/generate-pretty-string {:a 1 :b (LocalDate/parse "1953-05-29")})
"{\n \"a\" : 1,\n \"b\" : \"1953-05-29\"\n}")))
(testing "should generate a json string that has a java.time.Instant in it and not explode"
(is (= (ks-json/generate-pretty-string {:a 1 :b (LocalDateTime/parse "1954-07-31T21:00:30")})
"{\n \"a\" : 1,\n \"b\" : \"1954-07-31T21:00:30\"\n}"))))

(deftest test-generate-stream
(testing "should generate a json string from a stream"
(let [sw (StringWriter.)]
(generate-stream {:a 1 :b 2} sw)
(ks-json/generate-stream {:a 1 :b 2} sw)
(is (= (.toString sw)
"{\"a\":1,\"b\":2}")))))

(deftest test-generate-pretty-stream
(testing "should generate a pretty printed json string from a stream"
(let [sw (StringWriter.)]
(generate-pretty-stream {:a 1 :b 2} sw)
(ks-json/generate-pretty-stream {:a 1 :b 2} sw)
(is (= (.toString sw)
"{\n \"a\" : 1,\n \"b\" : 2\n}")))))

(deftest test-parse-string
(testing "should return a map from parsing a json string"
(is (= (parse-string "{\"a\":1,\"b\":2}")
(is (= (ks-json/parse-string "{\"a\":1,\"b\":2}")
{"a" 1 "b" 2}))))

(deftest test-parse-stream
(testing "should return map from parsing a json stream"
(is (= (parse-stream (StringReader. "{\"a\":1,\"b\":2}"))
(is (= (ks-json/parse-stream (StringReader. "{\"a\":1,\"b\":2}"))
{"a" 1 "b" 2}))))

(deftest test-spit-json
(let [json-out (temp-file "spit-json")]
(testing "json output with keywords"
(spit-json json-out {:a 1 :b 2})
(ks-json/spit-json json-out {:a 1 :b 2})
(is (= "{\n \"a\" : 1,\n \"b\" : 2\n}"
(slurp json-out))))
(testing "json output with strings"
(spit-json json-out {"a" 1 "b" 2})
(ks-json/spit-json json-out {"a" 1 "b" 2})
(is (= "{\n \"a\" : 1,\n \"b\" : 2\n}"
(slurp json-out))))))

0 comments on commit 1e371b1

Please sign in to comment.