Lorsque je récupère des valeurs d'une base de données Postgres avec clojure.java.jdbc, je me retrouve avec
{: id 1,: nom "exemple",: created_at #object [java.time.LocalDateTime 0x15e0aadc "2019-02-08T12: 52: 11.438633"]}
quand je veux
{: id 1,: nom "exemple",: created_at: created_at "2019-02-08T12: 52: 11.438633"]}
Je veux que la date soit renvoyée au format JSON et consommable par JavaScript. Le deuxième exemple n'est peut-être pas le format idéal pour cela et je suis donc ouvert aux autres, mais un objet Java ne fonctionnera pas.
J'ai essayé quelques approches, mais le compilateur a produit un certain nombre d'erreurs de «parenthèses sans correspondance» en raison de la date #object.
Des réflexions sur la façon de procéder? Dois-je faire quelque chose avec la requête SQL d'origine, est-ce que je fais quelque chose avec mes données renvoyées, ou autre chose?
3 Réponses :
J'imagine que cheshire.core / encode vous donnerait un joli JSON à partir de cette carte.
En savoir plus sur le cheshire à https://github.com/dakrone/cheshire p >
Je vais essayer!
J'ai essayé de recréer votre résultat avec la base de données H2, mais cela me donne un résultat Clojure #inst
au lieu d'une référence d'objet.
OK, voici le problème. Comme l'indique votre impression, vous avez un java.time.LocalDateTime
(un Clojure #inst
est un objet java.util.Date
).
Si vous voulez une version chaîne, tout ce que vous avez à faire est d'appeler la fonction membre toString
dessus:
(dotest (jdbc/db-do-commands db-conn ["drop table if exists langs"]) (jdbc/db-do-commands db-conn [(jdbc/create-table-ddl :langs [[:id :serial] [:lang "varchar not null"] [:creation :timestamptz]])]) (jdbc/insert-multi! db-conn :langs [{:lang "Clojure" :creation (tjt/iso-str->timestamp "2008-01-01T12:34:56Z")} {:lang "Java" :creation (tjt/iso-str->timestamp "1995-06-01T07:08:09Z")}]) (let [result (jdbc/query db-conn ["select * from langs"])] (is= (vec result) [{:id 1, :lang "Clojure", :creation #inst "2008-01-01T12:34:56.000000000-00:00"} {:id 2, :lang "Java", :creation #inst "1995-06-01T07:08:09.000000000-00:00"}])))
Cependant , aucune information de fuseau horaire n'est jointe. Donc, vous voulez probablement un ZonedDateTime. NOUS ASSUMERONS LE FUSEAU HORAIRE UTC CI-DESSOUS (veuillez vérifier la configuration de votre serveur DB):
(ns tst.demo.jdbc-pool (:use demo.core tupelo.core tupelo.test) (:require [clojure.java.jdbc :as jdbc] [hikari-cp.core :as pool] [tupelo.java-time :as tjt] ) ) (def datasource-options-pg {:adapter "postgresql" :database-name "alan" :server-name "localhost" :port-number 5433 :username "alan" :password "secret" } ) (def ^:dynamic db-conn nil) (defn with-connection-pool "Creates and uses a connection for test function" [tst-fn] (let [datasource (pool/make-datasource datasource-options-pg)] (binding [db-conn {:datasource datasource}] (tst-fn) (pool/close-datasource datasource)))) ; close the connection - also closes/destroys the in-memory database (use-fixtures :once with-connection-pool) ; use the same db connection pool for all tests
Notez que le ZDT a un suffixe comme [UTC]
ajouté à la fin, vous souhaiterez peut-être le convertir en Instant, puis utiliser la méthode .toString
pour en obtenir une représentation sous forme de chaîne plus simple (ISO-8601).
Si cela ne vous dérange pas d'utiliser une bibliothèque externe pour rendre cela plus facile, la bibliothèque tupelo.java-time
a une fonction d'assistance très pratique: p>
(ns xyz (require [tupelo.java-time :as tjt] ... )) (tjt/string-date-time-iso zdt) => "2019-02-01T00:00:00Z"
Il existe de nombreuses autres fonctions d'assistance disponibles. Veuillez consulter la documentation sur l'API et les tests unitaires pour des exemples .
J'ai finalement réparé mon installation Postgres (j'ai dû réinitialiser le mot de passe pour faire fonctionner Hikari). Voici mon code de test:
(let [jud (Date.) ldt (LocalDateTime/parse "2019-02-01T00:00:00") zdt (.atZone ldt (ZoneId/of "UTC")) ; *** big assumption here! *** inst (.toInstant zdt) inst-str (.toString inst) ] ; a Clojure #inst is just a java.util.Date object jud => #inst "2019-02-09T19:38:30.088-00:00" ; a ZonedDateTime can print in 2 ways zdt => #object[java.time.ZonedDateTime 0x780af31 "2019-02-01T00:00Z[UTC]"] (.toString zdt) => "2019-02-01T00:00Z[UTC]" ; a java.time.Instant also prints in 2 ways: instant => #object[java.time.Instant 0x48301adb "2019-02-01T00:00:00Z"] instant-str => "2019-02-01T00:00:00Z"
Ce qui précède n'est que des éléments de configuration. Voici le test unitaire qui vérifie le comportement:
(let [ldt (LocalDateTime/parse "2019-02-01T00:00:00" )] (.toString ldt) => <#java.lang.String "2019-02-01T00:00">
Vous pouvez donc voir que j'obtiens toujours un résultat java.util.Date
, qui Clojure imprime avec le formatage #inst
. Je ne sais pas comment vous obtenez JDBC pour afficher le format LocalDateTime
.
Merci pour votre réponse vraiment complète! Le .toString a fonctionné. Le besoin UTC n'est pas encore envisagé. Je vais aussi essayer Tupelo.
Étendez java.time.Instant dans votre fichier db.core.clj. Voir https://rundis.github.io/blog/2015/clojure_dates.html a> pour des exemples sur l'extension des types et des protocoles. (extend-type java.time.Instant
jdbc/ISQLParameter
(set-parameter [v ^PreparedStatement stmt ^long idx]
(.setTimestamp stmt idx (Timestamp. (.toEpochMilli v)))))
(extend-protocol cheshire.generate/JSONable
java.time.Instant
(to-json [dt gen]
(cheshire.generate/write-string gen (str dt))))
Et qu'utilisez-vous pour sérialiser votre carte en JSON? Ne peut-il pas gérer les datetimes?
J'utilisais la fonction write-str clojure.data.json qui a produit l'erreur suivante:
1. Java.lang.Exception non gérée Je ne sais pas comment écrire du JSON de classe java.time.LocalDateTime