请在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"` 时,并不是打印为 "autonamespaced",它只是在冒号 `:` 后面打印了它的名称 (":out") 来标识它为一个关键字。你可以随意使用字符串来构建关键字
`(keyword "heh [{:a 1}]" "//::! :: ::")` 将产生一个具有命名空间 `"heh [{:a 1}]"` 和名称 `"//::! :: ::"` 的关键字,它被打印为 `:heh [{:a 1}]///::! :: ::`。

由于你的情况下无法清理你的数据,我建议使用 edn 以外的允许自定义不同类型的读取器和编写器的序列化。例如,请参阅 [transit](https://github.com/cognitect/transit-clj)
...