Skip to content

Creating custom handlers

Alex Miller edited this page May 6, 2014 · 5 revisions

Provided handlers

Fressian provides handlers for the following tags and their Java representation:

  • short, integer, long - java.lang.[Short,Integer,Long]
  • double, float - java.lang.[Double,Float]
  • boolean - java.lang.Boolean
  • byte[], int[], long[], float[], boolean[], double[], Object[]
  • string - java.lang.String
  • null
  • any - TaggedObject
  • list - java.util.List
  • map - java.util.HashMap
  • set - java.util.HashSet
  • uuid - java.util.uuid
  • regex - java.util.regex.Pattern
  • uri - java.net.URI
  • bigint - java.math.BigInteger
  • bigdec - java.math.BigDecimal
  • inst - java.util.Date

data.fressian adds an additional set of handlers that cover many common Clojure types:

  • char - java.lang.Character
  • ratio - clojure.lang.Ratio
  • record - Clojure records
  • symbol - clojure.lang.Symbol
  • key - clojure.lang.Keyword
  • bigint - clojure.lang.BigInt

Adding your own

To add your own tag or override existing tags, you must implement ReadHandler and WriteHandler and merge those mappings into the data.fressian mappings (which are used by default).

Example

Let's add support for the java.awt.Point class. First define the read handler and write handler for Point:

;;;; Example ns header setting up imports and requires
(ns mytest
  (:require [clojure.data.fressian :as fressian])
  (:import [java.awt Point]
           [java.io ByteArrayOutputStream ByteArrayInputStream]
           [org.fressian.handlers WriteHandler ReadHandler]))

;; This is the bytecode "tag" for our new type
(def point-tag "point")

;; This WriteHandler will be invoked for an instance of Point
(def point-writer
  (reify WriteHandler
    (write [_ writer point]             ;; see org.fressian.Writer
      (.writeTag writer point-tag 2)    ;; write tag and expect 2 components
      (.writeInt writer (.x point))     ;; 1st component
      (.writeInt writer (.y point)))))  ;; 2nd component

;; This ReadHandler will be invoked when a "point" tag is read 
(def point-reader
  (reify ReadHandler
    (read [_ reader tag component-count]  ;; see org.fressian.Reader
      (Point. (.readInt reader) (.readInt reader)))))

;; Merge our handler (class -> tag -> writer) into standard clojure.write-handlers
;; Then wrap with associative-lookup, and inheritance-lookup
(def my-write-handlers
  (-> (merge {java.awt.Point {point-tag point-writer}}
             fressian/clojure-write-handlers)
      fressian/associative-lookup
      fressian/inheritance-lookup))

;; Merge our handler (tag -> reader) into standard clojure-read-handlers
;; Then wrap with associative-lookup
(def my-read-handlers
  (-> (merge {point-tag point-reader} fressian/clojure-read-handlers)
      fressian/associative-lookup))

;; Demo roundtrip of Point
(defn demo []
  (let [baos (ByteArrayOutputStream.)
        ;; create writer with my-write-handlers
        writer (fressian/create-writer baos :handlers my-write-handlers)
        point (Point. 1 2)]
    (fressian/write-object writer point)
    (let [bais (ByteArrayInputStream. (.toByteArray baos))
          ;; create reader with my-read-handlers
          reader (fressian/create-reader bais :handlers my-read-handlers)
          out (fressian/read-object reader)]
      (assert (= point out))))) ;; yup, got the same Point data!
Clone this wiki locally