Как я могу использовать *data-readers* с edn?

Я попытался следовать документации для clojure.instant/read-instant-timestamp, которая гласит:

clojure.instant/read-instant-timestamp
  To read an instant as a java.sql.Timestamp, bind *data-readers* to a
map with this var as the value for the 'inst key. Timestamp preserves
fractional seconds with nanosecond precision. The timezone offset will
be used to convert into UTC.`

Неожиданным оказался следующий результат:


(do
  (require '[clojure.edn :as edn])
  (require '[clojure.instant :refer [read-instant-timestamp]])
  (let [instant "#inst \"1970-01-01T00:00:09.999-00:00\""
        reader-map {'inst #'read-instant-timestamp}]
    ;; This binding is not appearing to do anything.
    (binding [*data-readers* reader-map]
      ;; prints java.util.Date -- unexpected
      (->> instant edn/read-string class println)
      ;; prints java.sql.Timestamp -- as desired
      (->> instant (edn/read-string {:readers reader-map}) class println))))

Как я могу использовать привязку *data-readers*? Кложур версии 1.5.1.


person ToBeReplaced    schedule 07.06.2013    source источник


Ответы (2)


Динамическая переменная *data-readers*, по-видимому, применяется только к функциям read-string и read из clojure.core.

(require '[clojure.instant :refer [read-instant-timestamp]])
(let [instant "#inst \"1970-01-01T00:00:09.999-00:00\""
      reader-map {'inst #'read-instant-timestamp}]
  ;; This will read a java.util.Date
  (->> instant read-string class println)
  ;; This will read a java.sql.Timestamp
  (binding [*data-readers* reader-map]
    (->> instant read-string class println)))

Просмотр исходного кода для clojure.edn reader здесь, я не смог найти ничего, что указывало бы на то, что там вообще используется одна и та же переменная *data-readers*.

функции clojure.core read и read-string используйте LispReader (в котором используется значение из *data-readers*), а функции из clojure.edn используйте EdnReader.

Эта библиотека edn является относительно новой в Clojure, поэтому может быть причиной того, что строка документации недостаточно конкретна в отношении edn и core читателя, что может вызвать такую ​​путаницу.

Надеюсь, поможет.

person juan.facorro    schedule 07.06.2013

clojure.edn по умолчанию используют только считыватели данных, хранящиеся в clojure.core/default-data-readers, который, начиная с Clojure 1.5.1, предоставляет считыватели для мгновенных литералов и литералов UUID. Если вы хотите использовать настраиваемые считыватели, вы можете сделать это, передав параметр :readers; в частности, вы можете пройти в *data-readers*. Это задокументировано в строке документации для clojure.edn/read (строка документации для clojure.edn/read-string относится к строке документации для read).

Вот некоторые примеры:

(require '[clojure.edn :as edn])

;; instant literals work out of the box:
(edn/read-string "#inst \"2013-06-08T01:00:00Z\"")
;= #inst "2013-06-08T01:00:00.000-00:00"

;; custom literals can be passed in in the opts map:
(edn/read-string {:readers {'foo identity}} "#foo :asdf")
;= :asdf

;; use current binding of *data-readers*
(edn/read-string {:readers *data-readers*} "...")

(Следующий раздел добавлен в ответ на комментарии Richard Möhn в эта ветка комментариев GitHub. Непосредственный вопрос заключается в том, уместно ли функции чтения вызывать eval в данные переданы. Я не связан с рассматриваемым проектом; подробности см. в билете, а также в комментариях Ричарда к данному ответу.)

Стоит добавить, что *data-readers* неявно заполняется из любых файлов data_readers.{clj,cljc}, которые Clojure находит в корне пути к классам во время запуска. Это может быть удобно (позволяет использовать настраиваемые помеченные литералы в исходном коде Clojure и в REPL), но это означает, что там могут появиться новые считыватели данных с изменением одной зависимости. Использование явно сконструированной карты чтения с clojure.edn — это простой способ избежать неожиданностей (которые могут быть особенно неприятными при работе с ненадежными входными данными).

(Обратите внимание, что неявный процесс загрузки не приводит к немедленной загрузке какого-либо кода или даже при первом обнаружении тега, упомянутого в *data-readers*; процесс, который заполняет *data-readers*, создает пустые пространства имен с несвязанными Vars в качестве заполнителей, и для фактического использования этих считывателей все еще необходимо require соответствующие пространства имен в пользовательском коде.)

person Michał Marczyk    schedule 07.06.2013