-
Notifications
You must be signed in to change notification settings - Fork 20
Getting Started
In order to read Transit encoded JSON you need to construct a reader:
(ns try-transit
(:require [cognitect.transit :as t]))
(def r (t/reader :json))
Currently :json
is the only type of reader available. Given a
a reader you can invoke cognitect.transit/read
.
For example reading a vector:
(def v (t/read r "[1,2,3]")) ;; [1 2 3]
And reading a map:
(def m (t/read r "{\"~:foo\":\"bar\"}")) ;; {:foo "bar"}
Like reading, writing is fairly straightforward. Constructing a writer looks very much like constructing a reader:
(ns try-transit
(:require [cognitect.transit :as t]))
(def w (t/writer :json))
Once you've constructed a writer you can invoke
cognitect.transit/write
to encode values.
For example writing a vector:
(t/write w [1 2 3]) ;; "[1,2,3]"
And writing a map:
(t/write w {:foo "bar"}) ;; "[\"^ \",\"~:foo\",\"bar\"]"
Maps get written out as JSON arrays as this form is more efficient for decoding. For debugging purposes it's useful to construct a verbose writer:
(def wv (t/writer :json-verbose))
And now the result of writing map-like values is easier to read:
(t/write wv {:foo "bar"}) ;; "{\"~:foo\":\"bar\"}"
This will return the string "{\"~:foo\":\"bar\"}"
.
Being able to easily write out graphs of ClojureScript values is one the big benefits of transit-cljs. transit-cljs will recursively encode graphs of values and Transit ground values like integers and dates need no special treatment.
To demonstrate this lets define some simple geometry primitives:
(deftype Rect [orgiin size])
(deftype Point [x y])
(deftype Size [width height])
(def arect (Rect. (Point. 0 0) (Size. 150 150)))
In order to write out aRect
we need write handlers for all
of the types involved. First let's write a handler
for Point
:
(deftype PointHandler []
Object
(tag [this v] "point")
(rep [this v] #js [(.-x v) (.-y v)])
(stringRep [this v] nil))
Write handlers are simplest to write with deftype
. Custom types
always become tagged values on the wire and the handler methods
specify how your instance will become a Transit tagged value. Write
handlers must supply at least the first two of the three methods:
tag
, rep
, and stringRep
. Each handler method receives the
value v
to be written.
tag
should be a method that will take the value and return a
string based tag. You can of course use the value argument v
to
write out different tags if you like but we're going to keep it simple
here.
rep
is the representation to use for the tagged value. In this case
we simply return an array containing the x
and y
properties. These
properties are numbers, a ground type, so there's nothing more for us
to do. It's important that the result of rep
be something that
transit-cljs already knows how to encode either via a built-in or
provided custom handler.
stringRep
is for tagged values that have a sensible representation
as JSON object keys (strings). For the most part you can omit this
method but we've left it here for completeness.
Now we can construct the following verbose writer and write Point
instances:
(def w
(t/writer :json-verbose
{:handlers {Point (PointHandler.)}}))
(t/write w (Point. 1.5 2.5)) ;; => "{\"~#point\":[1.5,2.5]}"
Now let's write the handlers for Size
and Rect
:
(deftype SizeHandler []
Object
(tag [this v] "size")
(rep [this v] #js [(.-width v) (.-height v)])
(stringRep [this v] nil))
(deftype RectHandler []
Object
(tag [this v] "rect")
(rep [this v] #js [(.-origin v) (.-size v)])
(stringRep [this v] nil))
That's it, we can now write out Rect
instances!
(def w
(t/writer :json-verbose
{:handlers {Point (PointHandler.)
Size (SizeHandler.)
Rect (RectHandler.)}})
(t/write w arect) ;; => "{\"~#rect\":[null,{\"~#size\":[150,150]}]}"