请在2024 年 Clojure 调查报告!中分享您的想法。

欢迎!请访问关于页面以了解更多此平台的工作信息。

0
命名空间和变量
编辑

我在使用pr-str序列化一个可能包含带自动命名空间关键词的数据结构。

我希望在序列化之前,减少数据结构不包含自动关键词,但不知道如何检测它?

(def x ::outbound)

(pr-str x) => "::outbound"

(edn/read-string "::outbound") => exception

(namespace x) => "current-namespace" 

那么,是否有方法知道x是否有自动命名空间?

谢谢,

抱歉 - 没有在repl上运行就写了这个问题 - 这里有一个合适的例子

(keyword ":out") => ::out

(pr-str (keyword ":out")) => "::out"

(edn/read-string (pr-str (keyword ":out"))) =>

Execution error at user/eval93457 (form-init751239350220000799.clj:1).
Invalid token: ::out

2 个答案

+2

自动解析的命名空间不是关键词的属性。关键词只有一个命名空间和名称。自动解析的(::)关键词在读取时解析为具有命名空间的完全限定关键词。Clojure 打印器永远不会打印带有::的关键词。

如果您想自定义打印器或使用其他函数来检测关键词的命名空间与当前命名空间相同,并将其作为自动解析的关键词打印,这是可能的(但几乎肯定不是一个好主意)。

edn/read-string不能读取自动解析的关键词,因为edn规范不包含自动解析的关键词。

谢谢,我想也是,但我某种原因可以复现这个问题,我会深入挖掘,一旦找到问题所在我会继续提问。

谢谢。

Timo
+1

编辑

自动命名空间的关键词在读取时会被扩展为普通命名空间的关键词,因此一旦你在数据结构中有了它们,它们就会带有命名空间进行打印。

$ clj
Clojure 1.10.0
user=> (pr-str ::out)
":user/out"

你是如何使得 (pr-str ::out) 打印出 ::out 的?

编辑
你观察到的实际上是 Clojure 允许你构造那些在打印时不可读的关键词的事实。
(keyword ":out") 构造了一个没有任何命名空间、以 ":out" 为名称的关键词,因此当它以 "::out" 的形式打印时,并不是因为它被打印为“自动命名空间”,而是因为它在冒号 ":" 之后打印了它的名称(":out"),而冒号 ":" 识别它为一个关键词。你可以用字符串自由构造关键词。
(keyword "heh [{:a 1}]" "//::! :: ::") 将会生成一个有命名空间 "heh [{:a 1}]" 和名称 "//::! :: ::" 的关键词,它看起来是这样的 :heh [{:a 1}]///::! :: ::

由于在你的情况下无法清洗你的数据,我建议使用允许对不同类型进行自定义读取器和编写器的序列化方法,而不是 edn。例如,看看 transit


编辑
(keyword ":out") => ::out

(pr-str (keyword ":out")) => "::out"

(edn/read-string (pr-str (keyword ":out"))) =>

在用户/eval93457 (form-init751239350220000799.clj:1) 处执行错误。
无效的令牌: ::out

显然在我们的情况下,输入是动态的 - 我们现在的问题是它存储在Datomic历史中,无法删除 - 因此我们需要一种方式来修复序列化或反序列化的方法。

clojure.core/read-string 将字符串评估为正确的关键字,但它会引发安全问题,因此不应使用
哦,这就是问题所在。你观察到的是 Clojure 允许你构建在打印时不可读的关键字这一事实。
`(keyword ":out")` 构造了一个没有名称空间而名称为 `:out` 的关键字,所以当它被打印为 `"::out"` 时,它并不是“自动名称空间”而只是打印其名称(`:out`),以冒号 `:` 作为关键字标识。你可以用字符串构造任意关键字的名称
`(keyword "heh [{:a 1}]" "//::! :: ::")` 将产生一个具有名称空间 `"heh [{:a 1}]"` 和名称 `"//::! :: ::"` 的关键字,它被打印为 `:heh [{:a 1}]///::! :: ::`。

因为你无法清理你的数据,所以我建议使用允许为不同类型提供自定义读取器和编写器的序列化方法,而不是 edn。例如,可以查看 [transit](https://github.com/cognitect/transit-clj)
...