Is there a reader function in clojure to parse clojure data structure? My use case is to read configuration properties files and one value for a property should be a list. I'd like to be able to write this as:
file.properties:
property1 = ["value1" "value2"]
and in clojure:
(load-props "file.properties")
and get a map with value {property1, ["value1" "value2"]
Right now,m I'm doing the following, with the same input file "file.properties":
(defn load-props [filename]
(let [io (java.io.FileInputStream. filename)
prop (java.util.Properties.)]
(.load prop io)
(into {} prop)))
;; returns:
;; {"property1" "[\"valu1\", \"valu2\"]"}
(load-props "file.properties")
But I cannot get a way to parse the result to a clojure's vector. I'm basically looking for something like Erlang's file:consult/1 function. Any idea how to do this?
If you want to read java-style properties files, look at Dave Ray's answer - though properties files have many limitations.
If you are using Clojure 1.5 or later, I suggest you use edn, the extensible data notation used in Datomic - it's basically clojure data structures, with no arbitrary code execution, and the ability to add tags for things like instances or arbitrary types.
The simplest way to use it is via read-string and slurp:
(require 'clojure.edn)
(clojure.edn/read-string (slurp "filename.edn"))
That's it. Note that read-string only reads a single variable, so you should set up your configuration as a map:
{ :property1 ["value1" "value2"] }
Then:
(require 'clojure.edn)
(def config (clojure.edn/read-string (slurp "config.edn")))
(println (:property1 config))
returns
["value1" "value2"]
java.util.Properties implements Map so this can be done very easily without manually parsing properties files:
(require 'clojure.java.io)
(defn load-props
[file-name]
(with-open [^java.io.Reader reader (clojure.java.io/reader file-name)]
(let [props (java.util.Properties.)]
(.load props reader)
(into {} (for [[k v] props] [(keyword k) (read-string v)])))))
(load-props "test.properties")
;=> {:property3 {:foo 100, :bar :test}, :property2 99.9, :property1 ["foo" "bar"]}
In particular, properties files are more complicated than you think (comments, escaping, etc, etc) and java.util.Properties is very good at loading them.
Is there a reader function in clojure to parse clojure data structure?
Yes. It's called read. You can also use it to read configuration data.
A file props.clj containing
{:property1 ["value1" 2]
:property2 {:some "key"}}
can be read like this:
(ns somens.core
(:require [clojure.java.io :as io])
(:import [java.io PushbackReader]))
(def conf (with-open [r (io/reader "props.clj")]
(read (PushbackReader. r))))
When reading untrusted sources it might be a good idea to turn of *read-eval*:
(def conf (binding [*read-eval* false]
(with-open [r (io/reader "props.clj")]
(read (PushbackReader. r)))))
For writing configuration data back to a file you should look at print functions such as pr and friends.
contrib has functions for reading writing properties,
http://richhickey.github.com/clojure-contrib/java-utils-api.html#clojure.contrib.java-utils/as-properties
If this is for your own consumption then I would suggest reading/writing clojure data structures you can just print them to disk and read them.
(use '[clojure.contrib.duck-streams :only (read-lines)])
(import '(java.io StringReader PushbackReader))
(defn propline->map [line] ;;property1 = ["value1" "value2"] -> { :property1 ["value1" "value2"] }
(let [[key-str value-str] (seq (.split line "="))
key (keyword (.trim key-str))
value (read (PushbackReader. (StringReader. value-str)))]
{ key value } ))
(defn load-props [filename]
(reduce into (map propline->map (read-lines filename))))
DEMO
user=> (def prop (load-props "file.properties"))
#'user/prop
user=> (prop :property1)
["value1" "value2"]
user=> ((prop :property1) 1)
"value2"
UPDATE
(defn non-blank? [line] (if (re-find #"\S" line) true false))
(defn non-comment? [line] (if (re-find #"^\s*\#" line) false true))
(defn load-props [filename]
(reduce into (map propline->map (filter #(and (non-blank? %)(non-comment? %)) (read-lines filename)))))
Related
I have a standard clojure map of things. The keys are keywords, and the values are arbitrary values. They can be nil, numbers, strings, or any other kind of JVM object/class.
I need to know how to encode this map into JSON, so that "normal" values map to the usual JSON values (e.g. keywords -> strings, integers -> JSON numbers, etc.), while values of any other class map to the string representations of those values, like this:
{
:a 1
:b :myword
:c "hey"
:d <this is an "unprintable" java File object>
}
gets encoded thus:
{ "a": 1, "b": "myword", "c": "hey", "d": "#object[java.io.File 0x6944e53e foo]" }
I want to do this because my program is a CLI parsing library and I'm working with the caller of the library to build this map up, so I don't exactly know what types of data will be in it. However, I would like to print it to the screen all the same to aid the caller in debugging. I have tried to naively give this map to cheshire, but when I do, cheshire keeps choking with this error:
Exception in thread "main" com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class java.io.File: foo
Bonus: I am trying to keep my dependency counts down and I have already vetted cheshire as my JSON library, but full marks if you can find a way to do the above without it.
With cheshire you can add an encoder for java.lang.Object
user> (require ['cheshire.core :as 'cheshire])
nil
user> (require ['cheshire.generate :as 'generate])
nil
user> (generate/add-encoder Object (fn [obj jsonGenerator] (.writeString jsonGenerator (str obj))))
nil
user> (def json (cheshire/generate-string {:a 1 :b nil :c "hello" :d (java.io.File. "/tmp")}))
#'user/json
user> (println json)
{"a":1,"b":null,"c":"hello","d":"/tmp"}
you can also override the print-method for some objects you're interested in:
(defmethod print-method java.io.File [^java.io.File f ^java.io.Writer w]
(print-simple (str "\"File:" (.getCanonicalPath f) "\"") w))
the method gets called by the print subsystem every time it needs to print out the object of this type:
user> {:a 10 :b (java.io.File. ".")}
;;=> {:a 10,
;; :b "File:/home/xxxx/dev/projects/clj"}
Cheshire includes Custom Encoders that you can create and register to serialize arbitrary classes.
OTOH if you want to read the JSON back and reproduce the same types back in Java, you'll need to add some metadata too. A common pattern is to encode the type as some field like __type or *class*, like this, so that the deserializer can find the right types:
{
__type: "org.foo.User"
name: "Jane Foo"
...
}
Unless I'm missing something, there is no need for JSON here. Just use prn:
(let [file (java.io.File. "/tmp/foo.txt")]
(prn {:a 1 :b "foo" :f file})
=> {:a 1,
:b "foo",
:f #object[java.io.File 0x5d617472 "/tmp/foo.txt"]}
Perfectly readable.
As Denis said, you'll need more work if you want to read back the data, but that is impossible for things like a File object anyway.
You can get the result as a string (suitable for println etc) using a related function pretty-str if you prefer:
(ns tst.demo.core
(:use tupelo.test)
(:require
[tupelo.core :as t] ))
(dotest
(let [file (java.io.File. "/tmp/foo.txt")]
(println (t/pretty-str {:a 1 :b "foo" :f file}))
))
=> {:a 1,
:b "foo",
:f #object[java.io.File 0x17d96ed9 "/tmp/foo.txt"]}
Update
Here is a technique I frequently use when I need to coerce data into another form, esp. for debugging or unit tests:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[clojure.walk :as walk]))
(defn walk-coerce-jsonable
[edn-data]
(let [coerce-jsonable (fn [item]
(cond
; coerce collections to simplest form
(sequential? item) (vec item)
(map? item) (into {} item)
(set? item) (into #{} item)
; coerce leaf values to String if can't JSON them
:else (try
(edn->json item)
item ; return item if no exception
(catch Exception ex
(pr-str item))))) ; if exception, return string version of item
result (walk/postwalk coerce-jsonable edn-data)]
result))
(dotest
(let [file (java.io.File. "/tmp/foo.txt")
m {:a 1 :b "foo" :f file}]
(println :converted (edn->json (walk-coerce-jsonable m)))
))
with result
-------------------------------
Clojure 1.10.1 Java 14
-------------------------------
Testing tst.demo.core
:converted {"a":1,"b":"foo","f":"#object[java.io.File 0x40429f12 \"/tmp/foo.txt\"]"}
Following code:
(set! (.. e -target -dataset -some-field) "some-value")
is compiled into:
return a.target.dataset.Qh=Yf(b)
some_field is compressed into Qh. And I need it to be exactly some_field.
I understand this is due to compression optimization. But is there a way to hint, or to bypass this behavior?
PS: simple optimization gives the desired output
return a.target.dataset.some_field=cljs.core.name.call(null,b)}
You may also be interested in the cljs-oops library: https://github.com/binaryage/cljs-oops
Then you can say:
(oset! el "innerHTML" "Hi!")
More examples below, and also on the CLJS Cheatsheet:
The problem is that some-field (or is it some_field or someField?) gets simplified to Qh. This is because the compiler does not know that the dataset object has a some-field property.
One solution is to write extern files so that the Google Closure Compiler will know that the given field must not be renamed.
An other solution is to use aset function or call goog.object.set function. This way you reference the field of the object with a string value and the string values do not get simplified.
Second example:
cljs.user=> (def a (clj->js {"a" 1}))
#'cljs.user/a
cljs.user=> a
#js {:a 1}
cljs.user=> (aset a "b" 2)
2
cljs.user=> a
#js {:a 1, :b 2}
I have a text file consisting of a json value in each line. My file is as follows:
{"id":"a","family":"root","parent":nil,"value":"valueofa"}
{"id":"b1","family":"b","parent":"a","value":"valueofb1"}
{"id":"c1","family":"c","parent":"b1","value":"valueofc1"}
{"id":"c2","family":"c","parent":"b1","value":"valueofc2"}
{"id":"b2","family":"b","parent":"root","value":"valueofb2"}
{"id":"d1","family":"d","parent":"b1","value":"valueofd1"}
In the json given above, we the family attribute indicates a hierarchy, we would have "root" as the root,"b" as child of the "root","c" as child of "b" and and "d" would be child of "b" as well.
The idea is to iterate through the file and add the node being read at its proper place in a tree. One way would be to read these entries into a "map" and then use this map for any tree-manipulations. For any complex tree manipulations, I am not sure how this would work. It is quite possible that based on a certain requirement, I might have to detach a child from an existing parent and attach it to another parent. Apparently Clojure zippers are supposed to help in this regard but I am getting a little confused by how the hierarchy of nodes work in zippers.
It will be great if somebody could point me in the right direction here.
This is several questions, and probably too broad, but here's a sampling.
How do I read a file?
(def file-contents (slurp "foo.txt"))`
How do I convert JSON to Clojure data?
(require '[cheshire.core :refer [parse-string]])`
(def data (map #(parse-string % true)
(clojure.string/split file-contents #"\n")))
How do I treat a list with parent references like a tree?
(require '[clojure.zip :as z])
(defn create-zipper [s]
(let [g (group-by :parent s)]
(z/zipper g #(map :id (g %)) nil (-> nil g first :id))))
(def t (create-zipper data))
See how to transform a seq into a tree
How do I use zippers?
user=> (-> t z/node)
"a"
user=> (-> t z/children)
("b1")
user=> (-> t z/down z/node)
"b1"
user=> (-> t z/down z/children)
("c1" "c2" "d1")
user=> (-> t z/down z/down z/rightmost z/node)
"d1"
Environment: Clojure 1.4
I'm trying to pull function metadata dynamically from a vector of functions.
(defn #^{:tau-or-pi: :pi} funca "doc for func a" {:ans 42} [x] (* x x))
(defn #^{:tau-or-pi: :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))
(def funcs [funca funcb])
Now, retrieving the metadata in the REPL is (somewhat) straight-forward:
user=>(:tau-or-pi (meta #'funca))
:pi
user=>(:ans (meta #'funca))
42
user=>(:tau-or-pi (meta #'funcb))
:tau
user=>(:ans (meta #'funcb))
43
However, when I try to do a map to get the :ans, :tau-or-pi, or basic :name from the metadata, I get the exception:
user=>(map #(meta #'%) funcs)
CompilerException java.lang.RuntimeException: Unable to resolve var: p1__1637# in this context, compiling:(NO_SOURCE_PATH:1)
After doing some more searching, I got the following idea from a posting in 2009 (https://groups.google.com/forum/?fromgroups=#!topic/clojure/VyDM0YAzF4o):
user=>(map #(meta (resolve %)) funcs)
ClassCastException user$funca cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I know that the defn macro (in Clojure 1.4) is putting the metadata on the Var in the def portion of the defn macro so that's why the simple (meta #'funca) is working, but is there a way to get the function metadata dynamically (like in the map example above)?
Maybe I'm missing something syntactically but if anyone could point me in the right direction or the right approach, that'd would be great.
Thanks.
the expression #(meta #'%) is a macro that expands to a call to defn (actually def) which has a parameter named p1__1637# which was produced with gensym and the call to meta on that is attempting to use this local parameter as a var, since no var exists with that name you get this error.
If you start with a vector of vars instead of a vector of functions then you can just map meta onto them. You can use a var (very nearly) anywhere you would use a function with a very very minor runtime cost of looking up the contents of the var each time it is called.
user> (def vector-of-functions [+ - *])
#'user/vector-of-functions
user> (def vector-of-symbols [#'+ #'- #'*])
#'user/vector-of-symbols
user> (map #(% 1 2) vector-of-functions)
(3 -1 2)
user> (map #(% 1 2) vector-of-symbols)
(3 -1 2)
user> (map #(:name (meta %)) vector-of-symbols)
(+ - *)
user>
so adding a couple #'s to your original code and removing an extra trailing : should do the trick:
user> (defn #^{:tau-or-pi :pi} funca "doc for func a" {:ans 42} [x] (* x x))
#'user/funca
user> (defn #^{:tau-or-pi :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))
#'user/funcb
user> (def funcs [#'funca #'funcb])
#'user/funcs
user> (map #(meta %) funcs)
({:arglists ([x]), :ns #<Namespace user>, :name funca, :ans 42, :tau-or-pi :pi, :doc "doc for func a", :line 1, :file "NO_SOURCE_PATH"} {:arglists ([x]), :ns #<Namespace user>, :name funcb, :ans 43, :tau-or-pi :tau, :doc "doc for func b", :line 1, :file "NO_SOURCE_PATH"})
user> (map #(:tau-or-pi (meta %)) funcs)
(:pi :tau)
user>
Recently, I found it useful to attach metadata to the functions themselves rather than the vars as defn does.
You can do this with good ol' def:
(def funca ^{:tau-or-pi :pi} (fn [x] (* x x)))
(def funcb ^{:tau-or-pi :tau} (fn [x] (* x x x)))
Here, the metadata has been attached to the functions and then those metadata-laden functions are bound to the vars.
The nice thing about this is that you no longer need to worry about vars when considering the metadata. Since the functions contain metadata instead, you can pull it from them directly.
(def funcs [funca funcb])
(map (comp :tau-or-pi meta) funcs) ; [:pi :tau]
Obviously the syntax of def isn't quite as refined as defn for functions, so depending on your usage, you might be interested in re-implementing defn to attach metadata to the functions.
I'd like to elaborate on Beyamor's answer. For some code I'm writing, I am using this:
(def ^{:doc "put the-func docstring here" :arglists '([x])}
the-func
^{:some-key :some-value}
(fn [x] (* x x)))
Yes, it is a bit unwieldy to have two metadata maps. Here is why I do it:
The first metadata attaches to the the-func var. So you can use (doc the-func) which returns:
my-ns.core/the-func
([x])
put the-func docstring here
The second metadata attaches to the function itself. This lets you use (meta the-func) to return:
{:some-key :some-value}
In summary, this approach comes in handy when you want both docstrings in the REPL as well as dynamic access to the function's metadata.
In Clojure, given a class name as a string, I need to create a new instance of the class. In other words, how would I implement new-instance-from-class-name in
(def my-class-name "org.myorg.pkg.Foo")
; calls constructor of org.myorg.pkg.Foo with arguments 1, 2 and 3
(new-instance-from-class-name my-class-name 1 2 3)
I am looking for a solution more elegant than
calling the Java newInstance method on a constructor from the class
using eval, load-string, ...
In practice, I will be using it on classes created using defrecord. So if there is any special syntax for that scenario, I would be quite interested.
There are two good ways to do this. Which is best depends on the specific circumstance.
The first is reflection:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
That's like calling (new Integer "16") ...include any other ctor arguments you need in the to-array vector. This is easy, but slower at runtime than using new with sufficient type hints.
The second option is as fast as possible, but a bit more complicated, and uses eval:
(defn make-factory [classname & types]
(let [args (map #(with-meta (symbol (str "x" %2)) {:tag %1}) types (range))]
(eval `(fn [~#args] (new ~(symbol classname) ~#args)))))
(def int-factory (make-factory "Integer" 'String))
(int-factory "42")
The key point is to eval code that defines an anonymous function, as make-factory does. This is slow -- slower than the reflection example above, so only do it as infrequently as possible such as once per class. But having done that you have a regular Clojure function that you can store somewhere, in a var like int-factory in this example, or in a hash-map or vector depending on how you'll be using it. Regardless, this factory function will run at full compiled speed, can be inlined by HotSpot, etc. and will always run much faster than the reflection example.
When you're specifically dealing with classes generated by deftype or defrecord, you can skip the type list since those classes always have exactly two ctors each with different arities. This allows something like:
(defn record-factory [recordname]
(let [recordclass ^Class (resolve (symbol recordname))
max-arg-count (apply max (map #(count (.getParameterTypes %))
(.getConstructors recordclass)))
args (map #(symbol (str "x" %)) (range (- max-arg-count 2)))]
(eval `(fn [~#args] (new ~(symbol recordname) ~#args)))))
(defrecord ExampleRecord [a b c])
(def example-record-factory (record-factory "ExampleRecord"))
(example-record-factory "F." "Scott" 'Fitzgerald)
Since 'new' is a special form, I'm not sure there you can do this without a macro. Here is a way to do it using a macro:
user=> (defmacro str-new [s & args] `(new ~(symbol s) ~#args))
#'user/str-new
user=> (str-new "String" "LOL")
"LOL"
Check out Michal's comment on the limitations of this macro.
Here is a technique for extending defrecord to automatically create well-named constructor functions to construct record instances (either new or based on an existing record).
http://david-mcneil.com/post/765563763/enhanced-clojure-records
In Clojure 1.3, defrecord will automatically defn a factory function using the record name with "->" prepended. Similarly, a variant that takes a map will be the record name prepended with "map->".
user=> (defrecord MyRec [a b])
user.MyRec
user=> (->MyRec 1 "one")
#user.MyRec{:a 1, :b "one"}
user=> (map->MyRec {:a 2})
#user.MyRec{:a 2, :b nil}
A macro like this should work to create an instance from the string name of the record type:
(defmacro newbie [recname & args] `(~(symbol (str "->" recname)) ~#args))