Clojure 2024 状态调查!中分享您的看法。

欢迎!请参阅关于页面了解如何操作的更多信息。

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 规范中不包括自动解析关键字。

谢谢,我心想如此,但不知何故我能复现这个问题,我会进一步深入了解并找到原因后再提出更多问题。

谢谢,

蒂莫
+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)
...