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

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

clojure.instant / чтения мгновенного Отметка времени
  Чтобы прочитать момент как java.sql.Timestamp, свяжите *data-reader * с
map с этим var в качестве значения для ключа 'inst. Отметка времени сохраняет
доли секунды с наносекундной точностью. Смещение часового пояса будет
использоваться для конвертации в 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* связывание? Clojure версия 1.5.1.

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 Читатель здесь, я не мог найти ничего, что указывало бы на то же *data-readers* там вообще используется вар.

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

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

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

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*} "...")

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

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

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

Другие вопросы по тегам