From d2c7a6db9343634b76bdcab61535400b50b199c2 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Sat, 5 Jun 2021 20:51:56 +0300 Subject: [PATCH 1/2] Add create-parser function and extend ReadValues for parsers --- src/clj/jsonista/core.clj | 52 +++++++++++++++++++++++++++++++++++-- test/jsonista/core_test.clj | 24 ++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/clj/jsonista/core.clj b/src/clj/jsonista/core.clj index 67b74d5..a886208 100644 --- a/src/clj/jsonista/core.clj +++ b/src/clj/jsonista/core.clj @@ -58,7 +58,8 @@ PersistentVectorDeserializer SymbolSerializer RatioSerializer FunctionalKeywordSerializer) - (com.fasterxml.jackson.core JsonGenerator$Feature JsonFactory) + (com.fasterxml.jackson.core JsonGenerator$Feature JsonFactory + JsonParser JsonToken) (com.fasterxml.jackson.databind JsonSerializer ObjectMapper SequenceWriter SerializationFeature DeserializationFeature Module) @@ -194,6 +195,41 @@ (-read-value [this ^ObjectMapper mapper] (.readValue mapper this ^Class Object))) +(defprotocol CreateParser + (-create-parser [this mapper])) + +(extend-protocol CreateParser + + (Class/forName "[B") + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) ^bytes this)) + + File + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) this)) + + URL + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) this)) + + String + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) this)) + + Reader + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) this)) + + InputStream + (-create-parser [this ^ObjectMapper mapper] + (.createParser (.getFactory mapper) this))) + +(defn ^JsonParser create-parser + ([this] + (-create-parser this default-object-mapper)) + ([this ^ObjectMapper om] + (-create-parser this om))) + (defprotocol ReadValues (-read-values [this mapper])) @@ -224,7 +260,19 @@ InputStream (-read-values [this ^ObjectMapper mapper] - (.readValues (.readerFor mapper ^Class Object) this))) + (.readValues (.readerFor mapper ^Class Object) this)) + + JsonParser + (-read-values [this _] + ;; This version is just for reading arrays. Should this + ;; also work with something else? + ;; Current token is empty (e.g. start of document) or just the token before array start + (assert (= JsonToken/START_ARRAY (.nextToken this))) + ;; Current token is START_ARRAY + (.nextToken this) + ;; Current token is the START_OBJECT for first object + ;; Should stop at the END_ARRAY + (.readValuesAs this ^Class Object))) (defprotocol WriteValue (-write-value [this value mapper])) diff --git a/test/jsonista/core_test.clj b/test/jsonista/core_test.clj index 5cfaeaa..e64efc4 100644 --- a/test/jsonista/core_test.clj +++ b/test/jsonista/core_test.clj @@ -268,7 +268,11 @@ (is (= original (j/read-values (str->input-stream input-string))))) (testing "Reader" - (is (= original (j/read-values (InputStreamReader. (str->input-stream input-string)))))))) + (is (= original (j/read-values (InputStreamReader. (str->input-stream input-string)))))) + + (testing "JsonParser" + (let [parser (j/create-parser input-string)] + (is (= original (j/read-values parser))))))) (deftest write-value-types (let [original {"ok" 1} @@ -344,3 +348,21 @@ (j/write-values file eduction) (is (= expected (slurp file))) (.delete file))) + +(deftest read-values-parser + (let [original {"type" "FeatureCollection" + "features" [{"value" 1} + {"value" 1} + {"value" 1}] + "foo" "bar"} + input-string (j/write-value-as-string original)] + ;; Ugh. Parser .readValuesAs works a bit different than ObjectReader .readValues + (testing "JsonParser" + (let [parser (j/create-parser input-string)] + ;; token = nil, start of document + (.nextToken parser) ;; START_OBJECT + (.nextToken parser) ;; "type" + (.nextToken parser) ;; "FeatureCollection" + (.nextToken parser) ;; "features" + (is (= (get original "features") + (j/read-values parser))))))) From a9500afe82b6a465ac86181226bd8ef2f690030e Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Fri, 1 Oct 2021 17:01:26 +0300 Subject: [PATCH 2/2] Remove read-values for JsonParser, make wrap-values public --- src/clj/jsonista/core.clj | 24 ++++++++++-------------- test/jsonista/core_test.clj | 12 ++++++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/clj/jsonista/core.clj b/src/clj/jsonista/core.clj index a886208..db4b753 100644 --- a/src/clj/jsonista/core.clj +++ b/src/clj/jsonista/core.clj @@ -225,6 +225,9 @@ (.createParser (.getFactory mapper) this))) (defn ^JsonParser create-parser + "Create an JsonParser using given ObjectMapper. + + See also: https://fasterxml.github.io/jackson-core/javadoc/2.10/com/fasterxml/jackson/core/JsonParser.html" ([this] (-create-parser this default-object-mapper)) ([this ^ObjectMapper om] @@ -260,19 +263,7 @@ InputStream (-read-values [this ^ObjectMapper mapper] - (.readValues (.readerFor mapper ^Class Object) this)) - - JsonParser - (-read-values [this _] - ;; This version is just for reading arrays. Should this - ;; also work with something else? - ;; Current token is empty (e.g. start of document) or just the token before array start - (assert (= JsonToken/START_ARRAY (.nextToken this))) - ;; Current token is START_ARRAY - (.nextToken this) - ;; Current token is the START_OBJECT for first object - ;; Should stop at the END_ARRAY - (.readValuesAs this ^Class Object))) + (.readValues (.readerFor mapper ^Class Object) this))) (defprotocol WriteValue (-write-value [this value mapper])) @@ -384,7 +375,12 @@ ([to object ^ObjectMapper mapper] (-write-value to object mapper))) -(defn- wrap-values +;; Calling iterator-seq in read-values would immediately initialize the seq. +;; Creating the Iterable allows creating the seq, when it is needed. +;; TODO: Name? +(defn wrap-values + "Wraps Jackson Iterator into Iterable, so it can be + converted to a seq automatically." [^Iterator iterator] (when iterator (reify diff --git a/test/jsonista/core_test.clj b/test/jsonista/core_test.clj index e64efc4..ec9b1fa 100644 --- a/test/jsonista/core_test.clj +++ b/test/jsonista/core_test.clj @@ -272,7 +272,10 @@ (testing "JsonParser" (let [parser (j/create-parser input-string)] - (is (= original (j/read-values parser))))))) + ;; token = nil, start of document + (.nextToken parser) ;; START_ARRAY + (.nextToken parser) ;; START_OBJECT + (is (= original (vec (j/wrap-values (.readValuesAs parser ^Class Object))))))))) (deftest write-value-types (let [original {"ok" 1} @@ -349,14 +352,13 @@ (is (= expected (slurp file))) (.delete file))) -(deftest read-values-parser +(deftest read-values-parser-example (let [original {"type" "FeatureCollection" "features" [{"value" 1} {"value" 1} {"value" 1}] "foo" "bar"} input-string (j/write-value-as-string original)] - ;; Ugh. Parser .readValuesAs works a bit different than ObjectReader .readValues (testing "JsonParser" (let [parser (j/create-parser input-string)] ;; token = nil, start of document @@ -364,5 +366,7 @@ (.nextToken parser) ;; "type" (.nextToken parser) ;; "FeatureCollection" (.nextToken parser) ;; "features" + (.nextToken parser) ;; START_ARRAY + (.nextToken parser) ;; START_OBJECT (the first item) (is (= (get original "features") - (j/read-values parser))))))) + (iterator-seq (.readValuesAs parser ^Class Object))))))))