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