Fork me on GitHub
#reitit
<
2019-05-03
>
conan14:05:19

Hi, I'm having trouble getting content-negotiation working in reitit using muuntaja. I can't write any java-time values as JSON. My understanding is that muuntaja hands off to jsonista, and that jsonista now supports java-time (as of 0.2.0, i'm using 0.2.2), but i'm thinking there's something i need to do to turn it on?

(j/write-value-as-string (jt/local-date-time))
Execution error (NoSuchMethodError) at com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase/createContextual (JSR310FormattedSerializerBase.java:123).
com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase.findFormatOverrides(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;Ljava/lang/Class;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value;

(import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule)

(j/write-value-as-string
  {:time (jt/local-date-time)}
  (j/object-mapper
    {:modules [(JavaTimeModule.)]}))
Execution error (NoSuchMethodError) at com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase/createContextual (JSR310FormattedSerializerBase.java:123).
com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase.findFormatOverrides(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;Ljava/lang/Class;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value;

conan14:05:53

This is me trying to get my head round it ^^^. In my route I've just defined a spec for the response and am letting the middleware do the content negotiation and coercion:

conan14:05:08

{:data {:coercion rcs/coercion
        :muuntaja muuntaja/instance
        :middleware [rrmm/format-middleware
                     rcoercion/coerce-exceptions-middleware
                     rcoercion/coerce-request-middleware
                     rcoercion/coerce-response-middleware]}
 :exception pretty/exception}

conan14:05:23

Am I wrong in thinking that java-time can be written as JSON by jsonista/muuntaja? If so I'll ditch it (cheshire can't do it either), but obviously that's a reasonable piece of work so I'd rather avoid it.

conan15:05:32

Apologies if this shouldn't be in the #reitit channel, it's how i got here but perhaps not the root of the issue

conan15:05:24

I've worked around the issue by adding the following middleware outside the routing tree, but it's a shame as that feels like the wrong place for it (it walks every response and that's not necessary for things without dates in them)

(defn wrap-replace-dates
  "Replaces java-time dates with strings"
  [handler]
  (fn [request]
    (let [response (handler request)]
      (update response :body #(walk/postwalk
                               (fn [node]
                                 (if (instance? LocalDateTime node)
                                   (str node)
                                   node))
                               %)))))

conan15:05:27

An alternative would be to write a different set of specs for API responses that mirror the originals but replace all the dates with strings, but that's a lot of extra spec to maintain

ikitommi06:05:02

If you can write an issue, I can look at it next week

conan12:05:23

ok thanks, i'll see if i can put together a minimal repro next week. have a fun weekend

conan09:05:40

Just putting one together now. I also notice that coercion doesn't work for path parameters - they always come as a string, even if my spec says int?. Maybe I've missed something that enables coercion?

ikitommi10:05:26

the coerced path-parameters are found under [:parameters :path] in the request

conan11:05:52

turns out all my jackson problems were a dependency mismatch, i think from amazonica or cheshire, both of which have jackson closer to the top of their dependency tree than reitit. exclusions have fixed it, sorry for the noise

ikitommi13:05:39

good to hear you got it solved.