I have a crazy idea, which involves putting some clojure code into CouchDB and writing views that query it. I don't want to store the clojure code as plain text, because then I would have to worry about parsing it in the views. Formatting and comments don't need to be preserved, but the code should be able to go in and out of the database without changing in structure. Keywords, symbols, and strings should all stay in their native type. Additionally, I want the code to look elegant and be efficient.
I'm thinking of representing things as follows:
Symbols as strings that start with '
Keywords as strings that start with :
Strings unmodified, except when they start with ' or :, in which case they're escaped with a backslash.
(parens) as an array
[brackets] as an array with "_[]" as the first element
maps ({}) as an object
sets (#{}) as an object with the values set to 1 and "_#{}" included.
Critiques, experiences, and ideas are appreciated.
Edit: Here's what happens if I try reading and writing JSON code using json functions from clojure.contrib:
user> code
((ns bz.json.app (:use (ring.middleware file))) (defn hello [req] {:status 200, :headers {"Content-Type" "text/plain"}, :body "Hello World!"}) (def app (wrap-file hello "public")))
user> (read-json (json-str code))
[["ns" "bz.json.app" ["use" ["ring.middleware" "file"]]] ["defn" "hello" ["req"] {"body" "Hello World!", "headers" {"Content-Type" "text/plain"}, "status" 200}] ["def" "app" ["wrap-file" "hello" "public"]]]
There's a fair bit that needs to be done for line 4 of the above to be exactly like line 2. It appears that it's a library project, unless there's a function somewhere that does it that I don't know about.
With such a library, here's what calling it might look like:
user> (= (json-to-code (read-json (json-str (code-to-json code)))) code)
true
As mikera suggested, clojure.contrib.json/write-json will convert not only primitive types, but Clojure's ISeqs and Java's Maps, Collections and Arrays, too. This should cover most of your code (seen as data), but in the event you want to write anything fancier, it's easy to extend the JSON writer, by mimicking out Stuart Sierra's source code (see here):
(defn- write-json-fancy-type [x #^PrintWriter out]
(write-json-string (str x) out)) ;; or something useful here!
(extend your-namespace.FancyType clojure.contrib.json/Write-JSON
{:write-json write-json-fancy-type})
This is assuming you don't need to store computed bytecode, or captured closures. This would be an entirely different game, significantly harder. But since most Clojure code (like most Lisp's) can be seen as a tree/forest of S-Expressions, you should be OK.
Parsing out JSON back to the data can be done with clojure.contrib.json/read-json (take a short time to look at the options on its definition, you may want to use them). After that, eval may be your best friend.
If you want to use JSON as a representation, I'd strongly suggest using clojure.contrib.json, which already does the job of converting Clojure data structures to JSON pretty seamlessly.
No point reinventing the wheel :-)
I've used it pretty successfully in my current Clojure project. If it doesn't do everything you want, then you can always contribute a patch to improve it!
I think your idea is sound, but I'd simplify the handling of collections by using tagged arrays (["list", …], ["vector", …]) instead. Apart from that, I wouldn't change the implementation strategy.
I like your idea and to code in Clojure, so I took a stab at implementing your code-to-json (with the above suggestion incorporated) at https://gist.github.com/3219854.
This is the output it generates:
(code-to-json example-code)
; => ["list" ["list" "'ns" "'bz.json.app" ["list" ":use" ["list" "'ring.middleware" "'file"]]] ["list" "'defn" "'hello" ["vector" "'req"] {":status" 200, ":headers" {"Content-Type" "text/plain"}, ":body" "Hello World!"}] ["list" "'def" "'app" ["list" "'wrap-file" "'hello" "public"]]]
json-to-code is left as an exercise for the reader. ;)
clojure.contrib.json has been superseded by clojure.data.json:
(require '[clojure.data.json :as json])
(json/write-str {:a 1 :b 2})
;;=> "{\"a\":1,\"b\":2}"
(json/read-str "{\"a\":1,\"b\":2}")
;;=> {"a" 1, "b" 2}
You might also like to use cheshire which has a nice API and support for various extensions such as custom encoding and SMILE (binary JSON):
(:require [cheshire.core :as json])
(json/encode {:a 1 :b 2})
;;=> "{\"a\":1,\"b\":2}"
(json/decode "{\"a\":1,\"b\":2}")
;;=> {"a" 1, "b" 2}
For completeness sake there is also clj-json which uses Jackson underneath to parse the JSON.
With version 0.1.2 of clojure.data.json the whole thing could look like this I guess:
(require ['clojure.data.json :as 'json])
(defn- json-write-date [s ^java.io.PrintWriter out escape-unicode?]
(.write out (str "\""
(.format (java.text.SimpleDateFormat. "yyyyMMddHHmmssZ") s) "\"")))
(extend java.util.Date clojure.data.json/Write-JSON {:write-json json-write-date})
(json/json-str { :example (java.util.Date.)})
"{\"example\":\"20120318182612+0100\"}"`
Related
I am using Common Lisp, SBCL, Emacs, and Slime.
In SLIME's REPL, I have a variable holding the file location:
CL-USER> json-file
#P"/home/pedro/lisp/json-example.json"
The content of the file is:
{"keyOne": "valueOne"}
I would like to read the data inside the file and return a string:
"{"keyOne": "valueOne"}"
How can I do it? Is it possible to do it with cl-json famous library?
The documentation was terse/hard. It is not good on providing examples. I couldn't find how to do it.
From what I read from the documentation, the API is built around streams. The function json:decode-json is taking a stream in parameter and return an association list which is very convenient to use.
To extract a value from the key, you can use the function (assoc :key-1 assoc-list). It will return a cons with (key . value). To get the value, you need to use the cdr function.
(defparameter json-string "{\"key-1\": \"value-1\"}")
(with-input-from-string (json-stream json-string)
(let ((lisp-data (json:decode-json json-stream)))
(cdr (assoc :key-1 lisp-data))))
Obviously, if you have the data in a file you could directly use the stream:
(with-open-file (json-stream "myfile.json" :direction :input)
(let ((lisp-data (json:decode-json json-stream)))
(cdr (assoc :key-1 lisp-data))))
Edit due to the comment of OP
The content of the file is:
{"keyOne": "valueOne"}
I would like to read the data inside the file and return a string:
"{"keyOne": "valueOne"}"
This question seems completely unrelated to the JSON library, but anyhow, if one need to open a file and put its content into a string, he can use a function from uiop.
* (uiop:read-file-string "test.txt")
"{\"keyOne\": \"valueOne\"}"
uiop is a library shipped with ASDF, so it's probably available to most Common Lisp's distributions. It's a kind of de-facto standard library and have a lot of interesting functions.
I have done the test on my computer, it's seems to work. I can certify that no data was harmed during that test.
I'm looking to modify some existing text in a web app, which I'm accessing with expressions like this:
(.-innerHTML (.getElementById js/document "myElementId"))
What I specifically want to do is mutate that text via a search/replace:
(set! (^that whole thing) (.replace (^that whole thing) "old" "new"))
And I'd ideally like to do that without having to repeat the property-access expression. Is there any existing shortcut for this? I'm envisioning something like Raku's object .= method(args) shorthand for object = object.method(args); maybe a good clj name/pattern would be (.set! (access-expression) method & args). I can always write a macro, but was wondering if I'd overlooked something already there.
I was curious too and looked in the CLJS CheatSheet and they link to the CLJS Oops library which provides similarly looking functions.
It seems to be that the most robust solution would be to just rely on the Google Closure Library (which is always at hand) and use a combination of goog.object/get and goog.object/set (no need for a macro), something like:
(require 'goog.object)
(defn update-obj! [obj field f & args]
(let [old-val (goog.object/get obj field)
new-val (apply f old-val args)]
(goog.object/set obj field new-val)))
;; Example:
(update-obj! (.getElementById js/document "my-div") "innerHTML" (partial str "it works: "))
This should work in both development and optimized output.
Is there a way to serialize functions at runtime in Clojure? I'd like to be able to send stateless (but not pure) functions over the wire in a serialized format (probably edn, but I'm open to anything).
For example...
If I run prn-str on a function, I don't get what I expected/wanted.
user=> (def fn1 (fn [x] (* x 2)))
#'user/fn1
user=> (def data {:test 1 :key "value"})
#'user/data
user=> (defn fn2 [x] (* x 2))
#'user/fn2
user=> (prn-str fn1)
"#object[user$fn1 0x28b9c6e2 \"user$fn1#28b9c6e2\"]\n"
user=> (prn-str data)
"{:test 1, :key \"value\"}\n"
user=> (prn-str fn2)
"#object[user$fn2 0x206c48f5 \"user$fn2#206c48f5\"]\n"
user=>
I would have wanted/expected something like this:
user=> (prn-str fn2)
"(fn [x] (* x 2))\n"
or, maybe,
user=> (prn-str fn2)
"(defn fn2 [x] (* x 2))\n"
You would have to use quote or ' to prevent evaluation and eval to force evaluation:
(def fn1 '(fn [x] (* x 2)))
(prn-str fn1) ;;=> "(fn [x] (* x 2))\n"
((eval fn1) 1) ;;=> 2
Flambo, a Clojure wrapper for Spark, uses the serializable-fn library to serialize functions (which Spark requires). Sparkling, another wrapper for Spark, uses native Clojure functions through this Java abstract class that implements the Java interface Serializable.
You have basically two choices:
pass source code (s-expressions stored as clojure data)
pass jar files and load them on the other side.
for the first option you save the source at the time the function is compiles (almost always when it is defined) and then pass the same source expression to the other computer and let it compile the same thing. so first you might make a vector of expressions:
(domain-functions '[(defn foo [x] x)
(defn bar [y] (inc y)]
then you can store this into a database and each client can pass it to read and then they will all have the same functions.
The second option depends on the fact that each time you define a function it produces a class file in the /target directory and then loads it. You can then syncronize this directory and load them on the other side. This approach is of course completely crazy, though people do crazy stuff around here. I recommend the first approach
And as a personal note:
I'm doing this now with datomic, and I have adopted the practice of putting the git-hash into the function name using a macro so I know absolutly for certain that when I call a function, I'm getting the same function I see in the editor. This brings peace of mind when running many instances that all pull from the same DB.
At some point it ceases to be Clojure, so the expectation that we can arbitrarily round trip from source to machine instructions and back is a little bit off.
We should be able to serialize a function to a byte array and send that across the wire though. I suspect you'd need to grab the function's java.lang.Class object and then pass that through a java.lang.instrument.ClassFileTransformer to get the bytes. Once you have those you can pass them through to the friendly java.lang.ClassLoader on the remote jvm.
You could use clojure.repl/source.
(with-out-str (source filter))
to get a string, or
(read-string (with-out-str (source filter)))
to get a clojure list.
There really isn't a good way, and for good reason simply shipping a function to another computer or storing it in a DB can cause lots of problems, not the least of which is that that function may require other functions that aren't on the other end.
A much better idea is to stick with data. Instead of writing the function, write the name of the function as a event, and then that even can be translated later by whatever is reading your data. Stick with data, that's the idiomatic way.
I want to jsonify the results of a query performed against a Postgres table containing a column of type text[], but the problem is that clojure.data.json.write-str doesn't seem to know how to handle PG arrays:
Exception Don't know how to write JSON of class org.postgresql.jdbc4.Jdbc4Array clojure.data.json/write-generic
Do I have to supply a custom handler, or is there a simpler way?
Just to wrap up, here's what works for me, putting together Jared's answer with the latest clojure.java.jdbc API changes (0.3.0-alpha5 at the time of writing) which deprecate some patterns that are commonly used (e.g. with-query-results).
First define additional handlers for every unsupported types you care about, for instance:
(extend-type org.postgresql.jdbc4.Jdbc4Array
json/JSONWriter
(-write [o out]
(json/-write (.getArray o) out)))
(extend-type java.sql.Timestamp
json/JSONWriter
(-write [date out]
(json/-write (str date) out)))
Then use with-open to perform the JSON encoding while the connection is still open (which is important at least for org.postgresql.jdbc4.Jdbc4Array):
(with-open [conn (jdbc/get-connection db)]
(json/write-str
(jdbc/query {:connection conn}
["SELECT * FROM ..."]))))
I guess this will change as the clojure.java.jdbc API evolves, but for the moment, it works.
It is better to extend IResultSetReadColumn because you are not
required to stay in the scope of with-open
Something along the following is also more concise:
(extend-protocol jdbc/IResultSetReadColumn
org.postgresql.jdbc4.Jdbc4Array
(result-set-read-column [pgobj metadata idx]
(.getArray pgobj)))
Make sure the namespace where this is placed-in is required!
cheshire is good about handling various data types and being extensible (see add-encoder)
It looks like the issue is org.postgresql.jdbc4.Jdbc4Array does not implement java.util.Collection. Try calling getArray before you serialize it.
Edit:
If it is a nested structure, then it might be best to implement a handler. clojure.data.json uses the JSONWriter protocol. You can try something like the following:
;; Note: Pseudo Code
(extend-type org.postgresql.jdbc4.Jdbc4Array
clojure.data.json/JSONWriter
(-write [o out] (clojure.data.json/-write (.getArray o) out)))
I need to create JSON objects from clojure maps that store things like clojure vars. The base implementation throws this kind of error when it sees them:
java.lang.Exception: Don't know how to write JSON of class clojure.lang.Var
Can anybody point me to sample code on how to extend the capabilities of the JSON writer?
Thanks.
Well, I figured out the answer. There's another SO question that answers it partially: How to map clojure code to and from JSON?
But here's the code that worked for me:
(defn- write-json-clojure-lang-var [x #^PrintWriter out]
(.print out (json-str (str x))))
(extend clojure.lang.Var clojure.contrib.json/Write-JSON
{:write-json write-json-clojure-lang-var})
Note that all I wanted to do is just render a string version of which Var I'm referring to. You could, of course, do many other things...
An update to the answer from zippy for those of us using the newer clojure.data.json. This is code that will work with the updated/new library:
(defn- write-json-clojure-lang-var [x #^PrintWriter out]
(.print out (json-str (str x))))
(extend clojure.lang.Var clojure.data.json/JSONWriter
{:-write write-json-clojure-lang-var})