Skip to content

1 Getting started

Peter Taoussanis edited this page May 30, 2024 · 4 revisions

Setup

Add the relevant dependency to your project:

Leiningen: [com.taoensso/nippy               "x-y-z"] ; or
deps.edn:   com.taoensso/nippy {:mvn/version "x-y-z"}

And setup your namespace imports:

(ns my-app (:require [taoensso.nippy :as nippy]))

De/serializing

As an example of what it can do, let's take a look at Nippy's own reference stress data:

{:nil                   nil
 :true                  true
 :false                 false
 :false-boxed (Boolean. false)

 :char      \ಬ
 :str-short "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"
 :str-long  (reduce str (range 1024))
 :kw        :keyword
 :kw-ns     ::keyword
 :sym       'foo
 :sym-ns    'foo/bar
 :kw-long   (keyword (reduce str "_" (range 128)) (reduce str "_" (range 128)))
 :sym-long  (symbol  (reduce str "_" (range 128)) (reduce str "_" (range 128)))

 :byte      (byte   16)
 :short     (short  42)
 :integer   (int    3)
 :long      (long   3)
 :float     (float  3.1415926535897932384626433832795)
 :double    (double 3.1415926535897932384626433832795)
 :bigdec    (bigdec 3.1415926535897932384626433832795)
 :bigint    (bigint  31415926535897932384626433832795)
 :ratio     22/7

 :list      (list 1 2 3 4 5 (list 6 7 8 (list 9 10 (list) ())))
 :vector    [1 2 3 4 5 [6 7 8 [9 10 [[]]]]]
 :subvec    (subvec [1 2 3 4 5 6 7 8] 2 8)
 :map       {:a 1 :b 2 :c 3 :d {:e 4 :f {:g 5 :h 6 :i 7 :j {{} {}}}}}
 :map-entry (clojure.lang.MapEntry/create "key" "val")
 :set       #{1 2 3 4 5 #{6 7 8 #{9 10 #{#{}}}}}
 :meta      (with-meta {:a :A} {:metakey :metaval})
 :nested    [#{{1 [:a :b] 2 [:c :d] 3 [:e :f]} [#{{[] ()}}] #{:a :b}}
             #{{1 [:a :b] 2 [:c :d] 3 [:e :f]} [#{{[] ()}}] #{:a :b}}
             [1 [1 2 [1 2 3 [1 2 3 4 [1 2 3 4 5 "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"] {} #{} [] ()]]]]]

 :regex          #"^(https?:)?//(www\?|\?)?"
 :sorted-set     (sorted-set 1 2 3 4 5)
 :sorted-map     (sorted-map :b 2 :a 1 :d 4 :c 3)
 :lazy-seq-empty (map identity ())
 :lazy-seq       (repeatedly 64 #(do nil))
 :queue-empty    (into clojure.lang.PersistentQueue/EMPTY [:a :b :c :d :e :f :g])
 :queue                clojure.lang.PersistentQueue/EMPTY

 :uuid       (java.util.UUID. 7232453380187312026 -7067939076204274491)
 :uri        (java.net.URI. "https://clojure.org")
 :defrecord  (nippy/StressRecord. "data")
 :deftype    (nippy/StressType.   "data")
 :bytes      (byte-array   [(byte 1) (byte 2) (byte 3)])
 :objects    (object-array [1 "two" {:data "data"}])

 :util-date (java.util.Date. 1577884455500)
 :sql-date  (java.sql.Date.  1577884455500)
 :instant   (java.time.Instant/parse "2020-01-01T13:14:15.50Z")
 :duration  (java.time.Duration/ofSeconds 100 100)
 :period    (java.time.Period/of 1 1 1)

 :throwable (Throwable. "Msg")
 :exception (Exception. "Msg")
 :ex-info   (ex-info    "Msg" {:data "data"})

 :many-longs    (vec (repeatedly 512         #(rand-nth (range 10))))
 :many-doubles  (vec (repeatedly 512 #(double (rand-nth (range 10)))))
 :many-strings  (vec (repeatedly 512         #(rand-nth ["foo" "bar" "baz" "qux"])))
 :many-keywords (vec (repeatedly 512
                       #(keyword
                          (rand-nth ["foo" "bar" "baz" "qux" nil])
                          (rand-nth ["foo" "bar" "baz" "qux"    ]))))}

Serialize it:

(def frozen-stress-data (nippy/freeze (nippy/stress-data {})))
=> #<byte[] [B@3253bcf3>

Deserialize it:

(nippy/thaw frozen-stress-data)
=> {:bytes        (byte-array [(byte 1) (byte 2) (byte 3)])
    :nil          nil
    :boolean      true
    <...> }

Couldn't be simpler!

Streaming

  • To serialize directly to a java.io.DataInput, see freeze-to-out!.
  • To deserialize directly from a java.io.DataOutput, see thaw-from-in!.

Encryption

You may want to consider using Nippy with Tempel for more comprehensive encryption options.

Nippy also gives you dead simple data encryption.
Add a single option to your usual freeze/thaw calls like so:

(nippy/freeze (nippy/stress-data {}) {:password [:salted "my-password"]}) ; Encrypt
(nippy/thaw   <encrypted-data>       {:password [:salted "my-password"]}) ; Decrypt

There's two default forms of encryption on offer: :salted and :cached. Each of these makes carefully-chosen trade-offs and is suited to one of two common use cases. See aes128-encryptor for a detailed explanation of why/when you'd want one or the other.

Custom types

It's easy to extend Nippy to your own data types:

(defrecord MyType [data])

(nippy/extend-freeze MyType :my-type/foo ; A unique (namespaced) type identifier
  [x data-output]
  (.writeUTF data-output (:data x)))

(nippy/extend-thaw :my-type/foo ; Same type id
  [data-input]
  (MyType. (.readUTF data-input)))

(nippy/thaw (nippy/freeze (MyType. "Joe"))) => #taoensso.nippy.MyType{:data "Joe"}
Clone this wiki locally