Welcome! Please see the About page for a little more info on how this works.

0 votes
in Namespaces and vars by
edited by

I'm serializing a data structure with pr-str that may have keywords with auto namespace

I would like to reduce the data structure not to contain auto keywords before serialization, but I don't know how to detect it?

(def x ::outbound)

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

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

(namespace x) => "current-namespace" 

so is there a way to know that x has auto namespace ?

thank you,

Sorry - wrote the question without running it on repl - a proper example here

(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 Answers

+2 votes
by

Auto-resolved namespacing is not an attribute of keywords. Keywords have just a namespace and a name. Autoresolved (::) keywords are resolved at read time into a fully-qualified keyword with a namespace. The Clojure printer will never print a keyword with ::.

If you wanted to customize the printer or use some other function to detect that a keyword's namespace is the same as your current namespace and print it as an autoresolved keyword, that's possible (but almost certainly not a very good idea).

edn/read-string won't read auto-resolved keywords as the edn spec does not include auto-resolved keywords.

by
Thanks, I thought so, but for some reason I'm able to reproduce this, I'll dig deeper into it and will ask more once I find the culprit.

Thanks,

Timo
+1 vote
by
edited by

Auto-namespaced keywords are expanded to normal namespaced keywords during read time, so once you have them in a data structure, they are printed with namespace:

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

How did you manage to make (pr-str ::out) print ::out?

Edit:
What you are observing is the fact that clojure allows you to contruct keywords that are not readable when they are printed.
(keyword ":out") constructs a keyword that has no namespace and ":out" as a name, so when it is printed as "::out", it's not that it's printed as "autonamespaced", it just prints it's name (":out") after colon ":" that identifies it as a keyword. You can go wild with constructing keywords from strings:
(keyword "heh [{:a 1}]" "//::! :: ::") will produce a keyword with namespace "heh [{:a 1}]" and name "//::! :: ::", it's printed like that :heh [{:a 1}]///::! :: ::.

Since in your case there is no way to clean your data, I'd suggest using serialization that allows custom readers and writers for different types instead of edn. Take a look at transit, for example

by
edited by
(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

obviously in our case the input is dynamic - the problem for us now is that it is stored in Datomic history and cannot be removed - thus we would need a way to fix either the serialization or deserialization somehow.

clojure.core/read-string evaluates the string and resolves the keyword correctly, but it opens up security problems so it should not be used
by
Oh, that's what's going on. What you are observing is the fact that clojure allows you to contruct keywords that are not readable when they are printed.
`(keyword ":out")` constructs a keyword that has no namespace and `":out"` as a name, so when it is printed as `"::out"`, it's not that it's printed as "autonamespaced", it just prints it's name (":out") after colon ":" that identifies it as a keyword. You can go wild with constructing keywords from strings:
`(keyword "heh [{:a 1}]" "//::! :: ::")` will produce a keyword with namespace `"heh [{:a 1}]"` and name `"//::! :: ::"`, it's printed like that `:heh [{:a 1}]///::! :: ::`.

Since in your case there is no way to clean your data, I'd suggest using serialization that allows custom readers and writers for different types instead of edn. Take a look at [transit](https://github.com/cognitect/transit-clj), for example
...