6

我尝试遵循 的文档clojure.instant/read-instant-timestamp,内容如下:

clojure.instant/read-instant-timestamp
  要将瞬间读取为 java.sql.Timestamp,请将 *data-readers* 绑定到
将此 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。

4

2 回答 2

12

clojure.edn默认情况下,函数仅使用存储在clojure.core/default-data-readers其中的数据读取器,从 Clojure 1.5.1 开始,为即时和 UUID 文字提供读取器。如果你想使用自定义阅读器,你可以通过传入一个:readers选项来实现;特别是,您可以传入*data-readers*. 这记录在文档字符串中clojure.edn/read(文档字符串 forclojure.edn/read-string指的是 for 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传入的数据。我不隶属于该项目有问题;请参阅票以获取详细信息,以及理查德对当前答案的评论。)

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

(请注意,隐式加载过程不会导致立即加载任何代码,甚至在*data-readers*第一次遇到中提到的标签时也不会导致;填充的过程*data-readers*会创建空的命名空间,其中未绑定的 Vars 作为占位符,并且要实际使用那些仍然有到require用户代码中的相关命名空间。)

于 2013-06-07T23:46:04.437 回答
5

*data-readers*动态 var 似乎仅适用于read-stringandread函数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.ednreader的源代码,我找不到任何表明那里使用了相同 var 的任何内容。*data-readers*

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

这个库在 Clojure 中相对较新,因此这可能是文档字符串在与readeredn方面不够具体的原因,这可能会导致这种混淆。edncore

希望能帮助到你。

于 2013-06-07T17:24:04.870 回答