diff --git a/src/clj/jsonista/core.clj b/src/clj/jsonista/core.clj index 649c998..800cc02 100644 --- a/src/clj/jsonista/core.clj +++ b/src/clj/jsonista/core.clj @@ -60,13 +60,13 @@ RatioSerializer FunctionalKeywordSerializer) (com.fasterxml.jackson.core JsonGenerator$Feature JsonFactory) (com.fasterxml.jackson.databind - JsonSerializer ObjectMapper module.SimpleModule + JsonSerializer ObjectMapper SequenceWriter SerializationFeature DeserializationFeature Module) (com.fasterxml.jackson.databind.module SimpleModule) (java.io InputStream Writer File OutputStream DataOutput Reader) (java.net URL) (com.fasterxml.jackson.datatype.jsr310 JavaTimeModule) - (java.util List Map Date) + (java.util List Map Date Iterator) (clojure.lang Keyword Ratio Symbol))) (defn- ^Module clojure-module @@ -194,6 +194,38 @@ (-read-value [this ^ObjectMapper mapper] (.readValue mapper this ^Class Object))) +(defprotocol ReadValues + (-read-values [this mapper])) + +(extend-protocol ReadValues + + (Class/forName "[B") + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) ^bytes this)) + + nil + (-read-values [_ _]) + + File + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) this)) + + URL + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) this)) + + String + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) this)) + + Reader + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) this)) + + InputStream + (-read-values [this ^ObjectMapper mapper] + (.readValues (.readerFor mapper ^Class Object) this))) + (defprotocol WriteValue (-write-value [this value mapper])) @@ -214,6 +246,50 @@ (-write-value [this value ^ObjectMapper mapper] (.writeValue mapper this value))) +(defprotocol WriteAll + (-write-all [this ^SequenceWriter writer])) + +(extend-protocol WriteAll + + (Class/forName "[Ljava.lang.Object;") + (-write-all [this ^SequenceWriter w] + (.writeAll w ^"[Ljava.lang.Object;" this)) + + Iterable + (-write-all [this ^SequenceWriter w] + (.writeAll w this))) + +(defprotocol WriteValues + (-write-values [this values mapper])) + +(defmacro ^:private -write-values* + [this value mapper] + `(doto ^SequenceWriter + (-write-all + ~value + (-> ~mapper + (.writerFor Object) + (.without SerializationFeature/FLUSH_AFTER_WRITE_VALUE) + (.writeValuesAsArray ~this))) + (.close))) + +(extend-protocol WriteValues + File + (-write-values [this value ^ObjectMapper mapper] + (-write-values* this value mapper)) + + OutputStream + (-write-values [this value ^ObjectMapper mapper] + (-write-values* this value mapper)) + + DataOutput + (-write-values [this value ^ObjectMapper mapper] + (-write-values* this value mapper)) + + Writer + (-write-values [this value ^ObjectMapper mapper] + (-write-values* this value mapper))) + ;; ;; public api ;; @@ -259,3 +335,54 @@ (-write-value to object default-object-mapper)) ([to object ^ObjectMapper mapper] (-write-value to object mapper))) + +(defn- wrap-values + [^Iterator iterator] + (reify + Iterable + (iterator [this] iterator) + Iterator + (hasNext [this] (.hasNext iterator)) + (next [this] (.next iterator)) + (remove [this] (.remove iterator)) + clojure.lang.IReduceInit + (reduce [_ f val] + (loop [ret val] + (if (.hasNext iterator) + (let [ret (f ret (.next iterator))] + (if (reduced? ret) + @ret + (recur ret))) + ret))) + clojure.lang.Sequential)) + +(defn read-values + "Decodes a sequence of values from a JSON as an iterator + from anything that satisfies [[ReadValue]] protocol. + By default, File, URL, String, Reader and InputStream are supported. + + The returned object is an Iterable, Iterator and IReduceInit. + It can be reduced on via [[reduce]] and turned into a lazy sequence + via [[iterator-seq]]. + + To configure, pass in an ObjectMapper created with [[object-mapper]], + see [[object-mapper]] docstring for the available options." + "Read a stream of values via an iterator. + Returned object is an Iterable, Iterator and reducible, similar to Eduction." + ([object] + (wrap-values (-read-values object default-object-mapper))) + ([object ^ObjectMapper mapper] + (wrap-values (-read-values object mapper)))) + +(defn write-values + "Encode values as JSON and write using the provided [[WriteValue]] instance. + By default, File, OutputStream, DataOutput and Writer are supported. + + By default, values can be an array or an Iterable. + + To configure, pass in an ObjectMapper created with [[object-mapper]], + see [[object-mapper]] docstring for the available options." + ([to object] + (-write-values to object default-object-mapper)) + ([to object ^ObjectMapper mapper] + (-write-values to object mapper)))