如何在edn中使用* data-readers *?

时间:2013-06-07 16:56:51

标签: clojure 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*绑定? Clojure版本1.5.1。

2 个答案:

答案 0 :(得分:12)

默认情况下,

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öhnthis GitHub issue的评论主题中所做的评论。当前的问题是读者函数是否适合调用{{ 1}}关于传入的数据。我不参与相关项目;请查看详细信息,以及理查德对目前答案的评论。)

值得补充的是,eval是从Clojure在启动时在类路径的根目录中找到的任何*data-readers*文件中隐式填充的。这可能很方便(它允许在Clojure源代码和REPL中使用自定义标记文字),但它确实意味着新数据读取器可能会出现在那里,并且单个依赖项发生了更改。使用明确构造的带有data_readers.{clj,cljc}的读者地图是一种避免意外的简单方法(在处理不受信任的输入时可能会特别讨厌)。

(请注意,隐式加载进程不会导致任何代码被立即加载,或者甚至在第一次遇到clojure.edn中提到的标记时;填充{{1使用未绑定的Vars创建空命名空间作为占位符,并且实际使用这些读者仍需要*data-readers*用户代码中的相关命名空间。)

答案 1 :(得分:5)

*data-readers*动态变量似乎仅适用于来自read-string的{​​{1}}和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))) 阅读器here的源代码时,我找不到任何可能表明在那里使用相同clojure.edn var的内容。

*data-readers*的函数readread-string使用LispReader(使用clojure.core中的值),而来自*data-readers*的函数使用EdnReader

clojure.edn库在Clojure中相对较新,因此可能是文档字符串对于ednedn读者不够具体的原因,这可能导致此类混乱。

希望它有所帮助。