1

My project parses JSONs, with a read/write library, called:

cheshire.core

I was having problems, trying to get the decode (func) to work, so I started messing around with:

data.json

My JSON contains data that consists of a field named "zone" this contains a vector with :keys inside, like so {:zone : [:hand :table]} that is stored into strings within the vector stored like so: {"zone" : ["hand" "table"]}

So I figured out how to convert the sample data using:

(mapv keyword {"zone" : ["hand"]})

which was great, I then needed to figure out how to implement a decoder for cheshire, I couldn't do this with my logic, I only spent like an hour working on this, but I had been using data.json, and the decoder function is relatively easy I think.

I got my project to work, here is some sample code:

(ns clojure-noob.core (:require
                    [cheshire.core :refer [decode]]
                    [clojure.data.json :as j-data]
                    ) (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  )

this is using cheshire:

(let [init (decode "{\"zone\" : [\"hand\"]}" true
               (fn [field-name]
                 (if (= field-name "zone")
                   (mapv keyword [])
                   [])))]
  (println (str init)))

this is using data.json:

(defn my-value-reader [key value]
  (if (= key :zone)
    (mapv keyword value)
      value))

(let [init (j-data/read-str
         "{\"zone\" : [\"hand\"]}"
         :value-fn my-value-reader
         :key-fn keyword)]
  (println (str init)))

I want the bottom result of these two from the console:

{:zone ["hand"]}
{:zone [:hand]}

The problem is I would like to do this using cheshire p.s. I am reading the factory section of cheshire? maybe this easier?

rezwits
  • 835
  • 7
  • 12
  • 2
    FWIW I'd consider just parsing the JSON using the defaults, then transforming the parsed output in a second pass. – Taylor Wood Sep 10 '18 at 23:31

2 Answers2

0

I would agree with @TaylorWood. Don't mess with the decoder, just do a bite in a time. First, parse json. Second, transform the result.

(def data "{\"zone\" : [\"hand\"]}")

(-> data 
    (cheshire.core/decode true)
    (update-in ["zone"] (partial mapv keyword)))
#=> {:zone [:hand]}
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • dang, looks nice, only just that I need the result to be {:zone [:hand]}, not "zone"... – rezwits Sep 11 '18 at 23:19
  • well I read excerpt from the cheshire author and he agrees: https://github.com/dakrone/cheshire/issues/49 – rezwits Sep 12 '18 at 00:31
  • I did notice that his decoder function ONLY works on the KEY that's being decoded... so I applied your fn and my key-fn but then this only ended up with the "zone" key in the being converted, nothing else is touched, gotta run... – rezwits Sep 12 '18 at 00:34
  • you can pass `true` to a `decode` function to keywordize keys – fl00r Sep 12 '18 at 08:59
0

I recommend you use a tool like schema.tools to coerce the input. You can add a second pass that attempts to coerce JSON strings into richer clojure types.

Here's some sample code!

;; require all the dependencies. See links below for libraries you need to add
(require '[cheshire.core :as json])
(require '[schema.core :as s])
(require '[schema.coerce :as sc])
(require '[schema-tools.core :as st])

;; your data (as before)
(def data "{\"zone\" : [\"hand\"]}")

;; a schema that wants an array of keywords
(s/defschema MyData {:zone [s/Keyword]})

;; use `select-schema` along with a JSON coercion matcher
(-> data
  (json/decode true)
  (st/select-schema MyData sc/json-coercion-matcher))

;; output: {:zone [:hand]}

Using defschema to define the shape of data you want gives you a general solution for serializing into JSON while getting the full benefit of Clojure's value types. Instead of explicitly "doing" the work of transforming, your schema describes the expected outcome, and hopefully coercions can do the right thing!

Links to libraries: - https://github.com/plumatic/schema - https://github.com/metosin/schema-tools#coercion

Note: you can do a similar thing with clojure.spec using metosin/spec-tools. Check out their readme for some help.

Ray H
  • 313
  • 3
  • 11