2

I'm have a following code:

(use 'clojure.java.io)
(defrecord Member [id name salary role])  
(defrecord Role [id name])
(def member-records (ref ()))
(defn add-member [member]
(dosync (alter member-records conj member)))

;;Test-data -->
(def dev-r(->Role 1 "Developer"))
(def test-member1(->Member 1 "Kirill" 70000.00 dev-r))
;;Test-data <--

(defn save-data-2-file []
  (with-open [wrtr (writer "C:/Platform/Work/test.cdf")]  
    (print-dup @member-records wrtr)))

(defn process-line [line]
  (println line))

 ;;Test line content
 ;;#BTC.pcost.Member{:id 1, :name "Kirill", :salary 70000.0, :role #BTC.pcost.Role{:id 1, :name "Developer"}})

(defn load-data-from-file []
  (with-open [rdr (reader "C:/Platform/Work/test.cdf")]
   (doseq [line (line-seq rdr)]
     (process-line line))))

I'm want to recreate records after reading file, but i can not understand how i can make it. Yes, i'm know that i can parse text and fill my structure by the elements of parsed line, but it's will be difficult, cause i'm have alot structs like "Member" and "Role". Can anyone to suggest me a way, that i can do?

2 Answers2

3

You can use read-string, and slurp, to pull the records out of the file. read-string is limited to reading the first form of a string, but, from your sample, you are only storing a single form, as a list of records.

(defn load-data-from-file [file]
  (read-string (slurp file)))


Lazy Reading

If you need more than the first form, or cannot read the entire stream into memory, you can use read directly, to make a lazy reader.

(defn lazy-read
  ([rdr] (let [eof (Object.)] (lazy-read rdr (read rdr false eof) eof)))
  ([rdr data eof]
    (if (not= eof data)
      (cons data (lazy-seq (lazy-read rdr (read rdr false eof) eof))))))

(defn load-all-data [file]
  (with-open [rdr (java.io.PushbackReader. (reader file))]
    (doall (lazy-read rdr))))

(load-all-data "C:/Platform/Work/test.cdf")


Security

Also, it is good to mention security when loading code with read-string or read. You should only use them with trusted sources, because, using #= or a Java constructor, the source can execute arbitrary code inside your application. For a longer explanation, take a look at the documentation for read.

Setting *read-eval* to false would prevent the issue, but it would also prevent the reconstruction of the records in your sample. To avoid the issue all together, you can use the clojure.edn/read and clojure.edn/read-string functions, with a whitelist of readers.

(defn edn-read [eof rdr]
  (clojure.edn/read {:eof eof :readers {'BTC.pcost.Role map->Role
                                        'BTC.pcost.Member map->Member}}
                    rdr))

(defn lazy-edn-read
  ([rdr] (let [eof (Object.)] (lazy-edn-read rdr (edn-read eof rdr) eof)))
  ([rdr data eof]
   (if (not= eof data)
     (cons data (lazy-seq (lazy-edn-read rdr (edn-read eof rdr) eof))))))

(defn load-all-data [file]
  (with-open [rdr (java.io.PushbackReader. (reader file))]
    (doall (take-while (complement nil?) (lazy-edn-read rdr)))))

(load-all-data "C:/Platform/Work/test.cdf")
Jared314
  • 5,181
  • 24
  • 30
2

You can use read.

This function will read one object from a file:

(defn load-data-from-file [filename]
  (with-open [rdr (java.io.PushbackReader. (reader filename))]
    (read rdr)))

Or this will read all objects from the file:

(defn load-all-data-from-file [filename]
  (let [eof (Object.)]
    (with-open [rdr (java.io.PushbackReader. (reader filename))]
      (doall
       (take-while #(not= % eof)
                   (repeatedly #(read rdr nil eof)))))))

Here's the API documentation for read.

This is a small variation that will read all objects from a string:

(defn load-all-data-from-string [string]
  (let [eof (Object.)]
    (with-open [rdr (-> string java.io.StringReader. java.io.PushbackReader.)]
      (doall
       (take-while #(not= % eof)
                   (repeatedly #(read rdr nil eof)))))))

This is, as far as I know, not possible to do using read-string. Instead we use read with a java.io.StringReader.

jbm
  • 2,575
  • 16
  • 15