【问题标题】:How can I use *data-readers* with edn?如何将 *data-readers* 与 edn 一起使用?
【发布时间】:2013-06-07 16:56:51
【问题描述】:

我尝试遵循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。

【问题讨论】:

    标签: clojure edn


    【解决方案1】:

    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 的评论线程中所做的 cmets。直接的问题是读取器函数是否适合在数据上调用 eval已通过。我与相关项目无关;详情请查看票证,以及当前答案中 Richard 的 cmets。)

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

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

    【讨论】:

      【解决方案2】:

      *data-readers* 动态变量似乎仅适用于来自 clojure.coreread-stringread 函数。

      (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 阅读器here 的源代码,我找不到任何表明在那里使用了相同的*data-readers* var 的东西。

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

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

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 2012-05-10
        • 2017-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多