cognitect / transit-cljs

Transit for ClojureScript
http://transit-format.org
Apache License 2.0
323 stars 20 forks source link

Round-trip fails when write handler returns extension type #23

Open stuartsierra opened 8 years ago

stuartsierra commented 8 years ago

It appears that when a write handler returns an object which is not a Transit ground type, the corresponding read handler gets called with a decoder.Tag value instead of the fully-decoded value.

This only applies in :json mode, not :json-verbose.

Sample ClojureScript code:

(ns example
  (:require [cognitect.transit :as transit]))

(defrecord VectorBox [value])

(defrecord ListBox [value])

;; This Transit write handler returns a Vector, which is a Transit
;; ground type.
(deftype VectorBoxWriteHandler []
  Object
  (tag [_ box] "vectorbox")
  (rep [_ box] (vec (:value box)))
  (stringRep [_ box] nil))

;; This Transit write handler returns a List, which is an extension
;; type in Transit, not a ground type.
(deftype ListBoxWriteHandler []
  Object
  (tag [_ box] "listbox")
  (rep [_ box] (list* (:value box)))
  (stringRep [_ box] nil))

(def my-write-handlers
  {VectorBox (VectorBoxWriteHandler.)
   ListBox (ListBoxWriteHandler.)})

(def my-read-handlers
  {"vectorbox" (fn [rep] (->VectorBox (vec rep)))
   "listbox" (fn [rep] (->ListBox (vec rep)))})

(defn round-trip
  "Returns the result of writing value with Transit and then reading
  it back. transit-type is either :json or :json-verbose"
  [value transit-type]
  {:pre [(contains? #{:json :json-verbose} transit-type)]}
  (transit/read
   (transit/reader transit-type {:handlers my-read-handlers})
   (transit/write
    (transit/writer transit-type {:handlers my-write-handlers})
    value)))

(def test-value {:listbox (->ListBox [1 2 3])
                 :vectorbox (->VectorBox [1 2 3])})

(defn test-round-trip-verbose
  "Asserts that we can successfully round-trip a value through transit
  using :json-verbose encoding."
  []
  (assert (= test-value (round-trip test-value :json-verbose))))

(defn test-round-trip-json
  "Asserts that we can successfully round-trip a value through transit
  using :json encoding. This test fails."
  []
  (assert (= test-value (round-trip test-value :json))))

Tested with these dependencies:

 [com.cognitect/transit-cljs "0.8.232"]
   [com.cognitect/transit-js "0.8.755"]
 [org.clojure/clojure "1.7.0"]
 [org.clojure/clojurescript "1.7.170"]