10

Transit format: An interactive tutorial - better than JSON (part 1)

 3 years ago
source link: https://blog.klipse.tech/clojure/2016/09/22/transit-clojure.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Transit format: An interactive tutorial - better than JSON (part 1)

Sep 22, 2016 • Yehonathan Sharvit

What is Transit?

Transit is a format and set of libraries for conveying values between applications written in different programming languages.

Transit provides:

  • a set of basic elements
  • a set of extension elements for representing typed values.

The extension mechanism is open, allowing programs using Transit to add new elements specific to their needs. It constrats with traditional formats where usually users of data formats must rely on one of the following:

  • schemas
  • convention
  • context

to convey elements not included in the base set, making application code much more complex.

Transit is designed to be implemented as an encoding on top of formats for which high performance processors already exist, specifically JSON and MessagePack. Transit uses these formats’ native representations for built-in elements, e.g., strings and arrays, wherever possible. Extension elements which have no native representation in these formats, e.g., dates, are represented using a tag-based encoding scheme. It makes Transit a self-describing and extensible format.

Read more about Transit.

Sandbox

Code examples please

All the above is very very abstract. Let’s code some examples.

In this article, we are going to use transit-cljs to illustrate Transit format.

As usual, the code snippets are interactive - powered by the KLIPSE plugin. Feel free to modify the code in order to get a better feeling of the concepts.

First, let’s require transit:

xxxxxxxxxx
(ns my.transit
  (:require [cognitect.transit :as t]))
the evaluation will appear here (soon)...

Now, we can play with transit

Let’s create a JSON reader and writer:

xxxxxxxxxx
(def r (t/reader :json))
(def w (t/writer :json))
xxxxxxxxxx
the evaluation will appear here (soon)...

And let’s see them in action:

Reader - basics

The reader receives a string in json format, and returns a clojure object:

Any type supported in json can go in…

Arrays, numbers, unicode strings, booleans, null:

xxxxxxxxxx
(t/read r "[1, \"2\", null, true, \"\u03BB\"]")
xxxxxxxxxx
the evaluation will appear here (soon)...

and obviously objects:

xxxxxxxxxx
(t/read r "{\"a\": 1, \"b\": [1, 2.2, 2e5, null]}")
xxxxxxxxxx
the evaluation will appear here (soon)...

Writer - basics

The writer is the opposite of the writer: it receives a clojure object and returns a string in json format:

Let’s write a clojure array:

xxxxxxxxxx
(t/write w [1 1.2e4 "2" nil true])
xxxxxxxxxx
the evaluation will appear here (soon)...

And a clojure object:

xxxxxxxxxx
(t/write w {"a" 1, "b" [1 2.2 200000 nil]})
xxxxxxxxxx
the evaluation will appear here (soon)...

Additional types

The interest of transit is that it supports additional types that are not supported in JSON like: keywords, symbols, dates, sets, lists:

Let’s see how keywords are encoded:

xxxxxxxxxx
(t/write w [:aaa :bbb :my.ns/bbb ::aaa])
xxxxxxxxxx
the evaluation will appear here (soon)...

Symbols:

xxxxxxxxxx
(t/write w ['aa 'bb])
xxxxxxxxxx
the evaluation will appear here (soon)...

Dates:

xxxxxxxxxx
(t/write w [(js/Date) #inst "2016-09-22T18:27:18.001-00:00"])
xxxxxxxxxx
the evaluation will appear here (soon)...

Sets:

xxxxxxxxxx
(t/write w #{1 2 3})
xxxxxxxxxx
the evaluation will appear here (soon)...

UUIDs:

xxxxxxxxxx
(t/write w (random-uuid))
xxxxxxxxxx
the evaluation will appear here (soon)...

Each of the above strings can be decoded back into a clojure object with the writer:

xxxxxxxxxx
(t/read r (t/write w [(js/Date) #inst "2016-09-22T18:27:18.001-00:00"]))
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
(t/read r (t/write w #{1 2 3}))
xxxxxxxxxx
the evaluation will appear here (soon)...

Any keys in an object

JSON suports only strings in keys. However in clojure, any type can be a key in a map. It means that we cannot truly serialize a clojure map. Usually, the trick is to convert types into strings. Like this:

xxxxxxxxxx
(def array-key-map {[1 2] "cool"})
(-> array-key-map
  clj->js
  js/JSON.stringify)
xxxxxxxxxx
the evaluation will appear here (soon)...

But when we read the string back, we don’t get the original object:

xxxxxxxxxx
(-> array-key-map
  clj->js
  js/JSON.stringify
  js/JSON.parse
  js->clj)
xxxxxxxxxx
the evaluation will appear here (soon)...

Transit solves this problem by encoding the types:

xxxxxxxxxx
(t/write w array-key-map)
xxxxxxxxxx
the evaluation will appear here (soon)...

And the roundtrip is safe:

xxxxxxxxxx
(t/read r (t/write w array-key-map))
xxxxxxxxxx
the evaluation will appear here (soon)...

In our next article , we are going to show how Transit deals with custom handlers and caching. You will see that in some cases, Transit is more efficient that JSON!

If you enjoy this kind of interactive articles would you consider a (small) donation💸 on Patreon or at least giving a star⭐ for the Klispe repo on Github?

to stay up-to-date with the coolest interactive articles around the world.

Discover more cool interactive articles about javascript, clojure[script], python, ruby, scheme, c++ and even brainfuck!

Give Klipse a Github star to express how much you appreciate Code Interactivity.

Subscribe to the Klipse newsletter:

Feel free to email me [email protected] for getting practical tips and tricks in writing your first interactive blog post.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK