ClojureDocs

导航

命名空间

read

clojure.edn

自 1.5 起即可用
  • (read)
  • (read stream)
  • (read opts stream)
Reads the next object from stream, which must be an instance of
java.io.PushbackReader or some derivee.  stream defaults to the
current value of *in*.
 Reads data in the edn format (subset of Clojure data):
http://edn-format.org
 opts is a map that can include the following keys:
:eof - value to return on end-of-file. When not supplied, eof throws an exception.
:readers  - a map of tag symbols to data-reader functions to be considered before default-data-readers.
            When not supplied, only the default-data-readers will be used.
:default - A function of two args, that will, if present and no reader is found for a tag,
           be called with the tag and the value.
4 Examples
;; See one of the examples for clojure.core/pr for some uncommon Clojure values
;; that cannot be printed then read back to get the original values (or in
;; some cases, cannot be read back at all).

;; If you wish to transmit data that may contain such values, one suggestion
;; is to use the transit library: https://github.com/cognitect/transit-format
;; Note that it is not possible to pass the result of (clojure.java.io/reader) 
;; directly to (clojure.edn/read):
(require '[clojure.java.io :as io] '[clojure.edn :as edn])
=> nil
(edn/read (io/reader (.getBytes ":edn")))
ClassCastException java.io.BufferedReader cannot be cast to java.io.PushbackReader

;; Instead, you need to construct a java.io.PushbackReader instance
;; and pass that to (edn/read):
(edn/read (java.io.PushbackReader. (io/reader (.getBytes ":edn"))))
=> :edn
;; If you need to distinguish between I/O errors (permission denied, etc)
;; and syntax errors in the EDN itself (eg, unbalanced parentheses), you'll
;; want to catch different exceptions from (edn/read):

(defn load-edn
  "Load edn from an io/reader source (filename or io/resource)."
  [source]
  (try
    (with-open [r (io/reader source)]
      (edn/read (java.io.PushbackReader. r)))

    (catch java.io.IOException e
      (printf "Couldn't open '%s': %s\n" source (.getMessage e)))
    (catch RuntimeException e
      (printf "Error parsing edn file '%s': %s\n" source (.getMessage e)))))
;; At some point you'll want to serialize and deserialize java.time.LocalDate.
;; Here's how:

(defn localdate-rdr
  "Takes serialized Java object, tests for LocalDate, and returns LocalDate object."
  [[clazz _ object-string :as input]] ;; [java.time.LocalDate 861096493 "2021-04-22"]
  (if (= 'java.time.LocalDate clazz)
      ;; LocalDate string will be of form yyyy-mm-dd
      ;; which also matches the parse function.
      (java.time.LocalDate/parse object-string)
      ;; just returns input if serialized object is not LocalDate
      input))

(defn test-localdate-rdr
  []
  (let [today        (java.time.LocalDate/now)
        ;; seralized -> "#object[java.time.LocalDate 0x4479783b \"2021-04-22\"]"
        serialized   (pr-str today)
        deserialized (clojure.edn/read {:readers {'object localdate-rdr}}
                                       (java.io.PushbackReader.
                                         (java.io.StringReader. serialized)))]
  (.toString (.plusDays deserialized 1))))

user=> (test-localdate-rdr) 
"2021-04-23" ;; Today was 2021-04-22
See Also

Reads one object from the string s. Returns nil when s is nil or empty. Reads data in the edn for...

Added by MicahElliott
1 Note
    By , created 9.8 years ago

    For everyone struggling to provide custom reader functions to clojure.edn/read: you cannot do that through data_readers.clj. data_readers.clj can be used to provide custom reader functions for clojure.core/read and clojure.core/read-string, but not clojure.edn/read and clojure.edn/read-string.

    You look carefully and you'll find that the official documentation does say so: if you don't supply a map of :readers, ‘only the default-data-readers will be used’. But the information from data_readers.clj ends up in *data-readers* not default-data-readers. However, you'd have to look very carefully to find this out.

    (If you're not interested in silly stories, you can skip the following two paragraphs and continue with the last.)

    Now you might think: ‘then I'll just say (edn/read-string {:readers *data-readers*} <a stream>)’. On the first try this will probably fail, because you forgot to require the namespaces where the reader functions live. Okay, you've fixed that………

    Browsing from your smartphone now? I know, your home directory just got wiped. And the external drive with the backups that's always plugged in to your machine as well. Why's that? Well, the procedures from clojure.edn are supposed to be safe, to not execute any code, so that you can use them with data from untrusted sources. If you do want to have code executed (only from trusted sources, of course), you use the corresponding procedures from clojure.core. Now, there are people who know about data_readers.clj only being used by clojure.core/read and clojure.core/read-string, which aren't safe. So they don't make their reader functions safe either. guten-tag.core/read-tagged-val is such an example. It calls eval on the data it is passed. That means, if you use it with clojure.edn/read or clojure.edn/read-string, those will become as unsafe as their clojure.core counterparts.

    Sorry for the long story. The moral is: have a very careful look at the reader functions you provide to the procedures from clojure.edn. If they execute code that's passed to them, all safety is lost.