diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,11 +1,15 @@
-# 0.1.2
+# 0.2
 
-* Added `Data.Winery.Query`
-* The winery tool now supports a simple jq-like query language
+* Renamed `extract*With` to `extract*By` for consistency
+* Added `hPut`
+* Improved the performance
+* Added `-J` option to `winery` which exports a JSON
+* Decoder now throws `DecodeException` rather than error calls
 
 # 0.1.1
 
-* Optimised the code
+* Add `Data.Winery.Query`
+* The command line tool supports a simple query language
 
 # 0.1
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -149,12 +149,15 @@
 (De)serialisation of the datatype above using generic instances:
 
 ```
-serialise/winery                         mean 658.6 μs  ( +- 45.04 μs  )
-serialise/binary                         mean 1.056 ms  ( +- 58.95 μs  )
-serialise/serialise                      mean 258.8 μs  ( +- 5.654 μs  )
-deserialise/winery                       mean 706.4 μs  ( +- 52.41 μs  )
-deserialise/binary                       mean 1.393 ms  ( +- 56.71 μs  )
-deserialise/serialise                    mean 765.8 μs  ( +- 30.26 μs  )
+serialise/list/winery                    mean 830.4 μs  ( +- 126.1 μs  )
+serialise/list/binary                    mean 1.268 ms  ( +- 126.3 μs  )
+serialise/list/serialise                 mean 309.5 μs  ( +- 22.33 μs  )
+serialise/item/winery                    mean 248.9 ns  ( +- 22.84 ns  )
+serialise/item/binary                    mean 1.222 μs  ( +- 77.28 ns  )
+serialise/item/serialise                 mean 384.6 ns  ( +- 15.63 ns  )
+deserialise/winery                       mean 972.5 μs  ( +- 150.6 μs  )
+deserialise/binary                       mean 1.721 ms  ( +- 99.67 μs  )
+deserialise/serialise                    mean 957.3 μs  ( +- 80.95 μs  )
 ```
 
 Not bad, considering that binary and serialise don't encode field names.
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -2,10 +2,11 @@
 module Main where
 
 import Control.Monad
+import qualified Data.Aeson as JSON
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
 import Data.Text.Prettyprint.Doc
 import Data.Text.Prettyprint.Doc.Render.Terminal
-import Data.Void
 import qualified Data.Winery.Query as Q
 import Data.Winery.Query.Parser
 import Data.Winery.Term
@@ -15,13 +16,14 @@
 import System.Exit
 import System.IO
 import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
 import Text.Megaparsec
-import Text.Megaparsec.Char
 
 data Options = Options
   { streamInput :: Bool
   , separateSchema :: Maybe (Maybe FilePath)
   , printSchema :: Bool
+  , outputJSON :: Bool
   }
 
 defaultOptions :: Options
@@ -29,6 +31,7 @@
   { streamInput = False
   , printSchema = False
   , separateSchema = Nothing
+  , outputJSON = False
   }
 
 options :: [OptDescr (Options -> Options)]
@@ -36,6 +39,7 @@
   [ Option "s" ["stream"] (NoArg $ \o -> o { streamInput = True }) "stream input"
   , Option "S" ["separate-schema"] (OptArg (\s o -> o { separateSchema = Just s }) "PATH") "the schema is separated"
   , Option "" ["print-schema"] (NoArg $ \o -> o { printSchema = True }) "print the schema"
+  , Option "J" ["JSON"] (NoArg $ \o -> o { outputJSON = True }) "print as JSON"
   ]
 
 getRight :: Either StrategyError a -> IO a
@@ -49,7 +53,11 @@
 
 app :: Options -> Q.Query (Doc AnsiStyle) (Doc AnsiStyle) -> Handle -> IO ()
 app o q h = do
-  let getDec = getRight . getDecoderBy (Q.runQuery q (pure . pretty <$> decodeTerm))
+  let p
+        | outputJSON o = pretty . T.decodeUtf8 . BL.toStrict . JSON.encode
+        | otherwise = pretty
+  let getDec = getRight . getDecoderBy (Q.runQuery q (pure . p <$> decodeTerm))
+
   printer <- case separateSchema o of
     Just mpath -> do
       bs <- maybe (readLn >>= B.hGet h) B.readFile mpath
diff --git a/benchmarks/bench.hs b/benchmarks/bench.hs
--- a/benchmarks/bench.hs
+++ b/benchmarks/bench.hs
@@ -13,6 +13,7 @@
 import qualified Codec.Serialise as CBOR
 import qualified Data.Csv as CSV
 import Data.Winery.Term
+import System.Directory
 
 data Gender = Male | Female deriving (Show, Generic)
 
@@ -47,11 +48,23 @@
   binary <- B.readFile "benchmarks/data.binary"
   cbor <- B.readFile "benchmarks/data.cbor"
   values :: [TestRec] <- return $ B.decode $ BL.fromStrict binary
+  let aValue = head values
+  temp <- getTemporaryDirectory
   defaultMain
-    [ bgroup "serialise"
-      [ bench "winery" $ nf serialise values
+    [ bgroup "serialise/list"
+      [ bench "winery" $ nf serialiseOnly values
       , bench "binary" $ nf (BL.toStrict . B.encode) values
       , bench "serialise" $ nf (BL.toStrict . CBOR.serialise) values
+      ]
+    , bgroup "serialise/item"
+      [ bench "winery" $ nf serialiseOnly aValue
+      , bench "binary" $ nf (BL.toStrict . B.encode) aValue
+      , bench "serialise" $ nf (BL.toStrict . CBOR.serialise) aValue
+      ]
+    , bgroup "serialise/file"
+      [ bench "winery" $ whnfIO $ writeFileSerialise (temp ++ "/data.winery") values
+      , bench "binary" $ whnfIO $ B.encodeFile (temp ++ "/data.binary") values
+      , bench "serialise" $ whnfIO $ CBOR.writeFileSerialise (temp ++ "/data.cbor") values
       ]
     , bgroup "deserialise"
       [ bench "winery" $ nf (fromRight undefined . deserialise :: B.ByteString -> [TestRec]) winery
diff --git a/src/Data/Winery.hs b/src/Data/Winery.hs
--- a/src/Data/Winery.hs
+++ b/src/Data/Winery.hs
@@ -16,12 +16,14 @@
 module Data.Winery
   ( Schema(..)
   , Serialise(..)
+  , DecodeException(..)
   , schema
   -- * Standalone serialisation
   , serialise
   , deserialise
   , deserialiseBy
   , splitSchema
+  , writeFileSerialise
   -- * Separate serialisation
   , Deserialiser(..)
   , Decoder
@@ -33,12 +35,12 @@
   , encodeMulti
   -- * Decoding combinators
   , Plan(..)
-  , extractArrayWith
-  , extractListWith
+  , extractArrayBy
+  , extractListBy
   , extractField
-  , extractFieldWith
+  , extractFieldBy
   , extractConstructor
-  , extractConstructorWith
+  , extractConstructorBy
   , extractScientific
   -- * Variable-length quantity
   , VarInt(..)
@@ -63,6 +65,7 @@
   )where
 
 import Control.Applicative
+import Control.Exception
 import Control.Monad.Trans.Cont
 import Control.Monad.Reader
 import qualified Data.ByteString as B
@@ -95,6 +98,7 @@
 import Data.Text.Prettyprint.Doc.Render.Terminal
 import Data.Typeable
 import GHC.Generics
+import System.IO
 import Unsafe.Coerce
 
 data Schema = SSchema !Word8
@@ -228,6 +232,13 @@
   $ toEncoding (schema [a], a)
 {-# INLINE serialise #-}
 
+-- | Serialise a value along with its schema.
+writeFileSerialise :: Serialise a => FilePath -> a -> IO ()
+writeFileSerialise path a = withFile path WriteMode
+  $ \h -> BB.hPut h $ mappend (BB.word8 currentSchemaVersion)
+  $ toEncoding (schema [a], a)
+{-# INLINE writeFileSerialise #-}
+
 splitSchema :: B.ByteString -> Either StrategyError (Schema, B.ByteString)
 splitSchema bs_ = case B.uncons bs_ of
   Just (ver, bs) -> do
@@ -435,7 +446,7 @@
 
 instance (Typeable a, Bits a, Integral a) => Serialise (VarInt a) where
   schemaVia _ _ = SInteger
-  toEncoding = encodeVarInt . getVarInt
+  toEncoding = BB.varInt . getVarInt
   {-# INLINE toEncoding #-}
   deserialiser = Deserialiser $ Plan $ \case
     SInteger -> pure $ evalContT decodeVarInt
@@ -456,8 +467,8 @@
 instance Serialise a => Serialise (Maybe a) where
   schemaVia _ ts = SVariant [("Nothing", [])
     , ("Just", [substSchema (Proxy :: Proxy a) ts])]
-  toEncoding Nothing = encodeVarInt (0 :: Word8)
-  toEncoding (Just a) = encodeVarInt (1 :: Word8) <> toEncoding a
+  toEncoding Nothing = BB.varInt (0 :: Word8)
+  toEncoding (Just a) = BB.varInt (1 :: Word8) <> toEncoding a
   deserialiser = Deserialiser $ Plan $ \case
     SVariant [_, (_, [sch])] -> do
       dec <- unwrapDeserialiser deserialiser sch
@@ -481,10 +492,10 @@
     Nothing -> SList (substSchema (Proxy :: Proxy a) ts)
     Just s -> SArray (VarInt s) (substSchema (Proxy :: Proxy a) ts)
   toEncoding xs = case constantSize (Proxy :: Proxy a) of
-    Nothing -> encodeVarInt (length xs)
+    Nothing -> BB.varInt (length xs)
       <> encodeMulti (\r -> foldr (encodeItem . toEncoding) r xs)
-    Just _ -> encodeVarInt (length xs) <> foldMap toEncoding xs
-  deserialiser = extractListWith deserialiser
+    Just _ -> BB.varInt (length xs) <> foldMap toEncoding xs
+  deserialiser = extractListBy deserialiser
 
 instance Serialise a => Serialise (V.Vector a) where
   schemaVia _ = schemaVia (Proxy :: Proxy [a])
@@ -501,8 +512,8 @@
   toEncoding = toEncoding . UV.toList
   deserialiser = UV.fromList <$> deserialiser
 
-extractArrayWith :: Deserialiser a -> Deserialiser (Int, Int -> a)
-extractArrayWith (Deserialiser plan) = Deserialiser $ Plan $ \case
+extractArrayBy :: Deserialiser a -> Deserialiser (Int, Int -> a)
+extractArrayBy (Deserialiser plan) = Deserialiser $ Plan $ \case
   SArray (VarInt size) s -> do
     getItem <- unPlan plan s
     return $ evalContT $ do
@@ -514,12 +525,12 @@
       n <- decodeVarInt
       offsets <- decodeOffsets n
       asks $ \bs -> (n, \i -> decodeAt (offsets UV.! i) getItem bs)
-  s -> unexpectedSchema' "extractListWith ..." "[a]" s
+  s -> unexpectedSchema' "extractListBy ..." "[a]" s
 
 -- | Extract a list or an array of values.
-extractListWith :: Deserialiser a -> Deserialiser [a]
-extractListWith d = (\(n, f) -> map f [0..n-1]) <$> extractArrayWith d
-{-# INLINE extractListWith #-}
+extractListBy :: Deserialiser a -> Deserialiser [a]
+extractListBy d = (\(n, f) -> map f [0..n-1]) <$> extractArrayBy d
+{-# INLINE extractListBy #-}
 
 instance (Ord k, Serialise k, Serialise v) => Serialise (M.Map k v) where
   schemaVia _ = schemaVia (Proxy :: Proxy [(k, v)])
@@ -570,12 +581,12 @@
 
 -- | Extract a field of a record.
 extractField :: Serialise a => T.Text -> Deserialiser a
-extractField = extractFieldWith deserialiser
+extractField = extractFieldBy deserialiser
 {-# INLINE extractField #-}
 
 -- | Extract a field using the supplied 'Deserialiser'.
-extractFieldWith :: Typeable a => Deserialiser a -> T.Text -> Deserialiser a
-extractFieldWith (Deserialiser g) name = Deserialiser $ handleRecursion $ \case
+extractFieldBy :: Typeable a => Deserialiser a -> T.Text -> Deserialiser a
+extractFieldBy (Deserialiser g) name = Deserialiser $ handleRecursion $ \case
   SRecord schs -> do
     let schs' = [(k, (i, s)) | (i, (k, s)) <- zip [0..] schs]
     case lookup name schs' of
@@ -587,12 +598,12 @@
       Nothing -> errorStrategy $ rep <> ": Schema not found in " <> pretty (map fst schs)
   s -> unexpectedSchema' rep "a record" s
   where
-    rep = "extractFieldWith ... " <> dquotes (pretty name)
-    msg = "Data.Winery.extractFieldWith ... " <> show name <> ": impossible"
+    rep = "extractFieldBy ... " <> dquotes (pretty name)
+    msg = "Data.Winery.extractFieldBy ... " <> show name <> ": impossible"
 
 handleRecursion :: Typeable a => (Schema -> Strategy (Decoder a)) -> Plan (Decoder a)
 handleRecursion k = Plan $ \sch -> Strategy $ \decs -> case sch of
-  SSelf i -> return $ fmap (`fromDyn` error "Invalid recursion")
+  SSelf i -> return $ fmap (`fromDyn` throw InvalidTag)
     $ unsafeIndex "Data.Winery.handleRecursion: unbound fixpoint" decs (fromIntegral i)
   SFix s -> mfix $ \a -> unPlan (handleRecursion k) s `unStrategy` (fmap toDyn a : decs)
   s -> k s `unStrategy` decs
@@ -705,8 +716,8 @@
 
 -- | Tries to extract a specific constructor of a variant. Useful for
 -- implementing backward-compatible deserialisers.
-extractConstructorWith :: Typeable a => Deserialiser a -> T.Text -> Deserialiser (Maybe a)
-extractConstructorWith d name = Deserialiser $ handleRecursion $ \case
+extractConstructorBy :: Typeable a => Deserialiser a -> T.Text -> Deserialiser (Maybe a)
+extractConstructorBy d name = Deserialiser $ handleRecursion $ \case
   SVariant schs0 -> Strategy $ \decs -> do
     (j, dec) <- case [(i :: Int, ss) | (i, (k, ss)) <- zip [0..] schs0, name == k] of
       [(i, [s])] -> fmap ((,) i) $ unwrapDeserialiser d s `unStrategy` decs
@@ -720,10 +731,10 @@
         else pure Nothing
   s -> unexpectedSchema' rep "a variant" s
   where
-    rep = "extractConstructorWith ... " <> dquotes (pretty name)
+    rep = "extractConstructorBy ... " <> dquotes (pretty name)
 
 extractConstructor :: (Serialise a) => T.Text -> Deserialiser (Maybe a)
-extractConstructor = extractConstructorWith deserialiser
+extractConstructor = extractConstructorBy deserialiser
 {-# INLINE extractConstructor #-}
 
 -- | Generic implementation of 'schemaVia' for a record.
@@ -844,7 +855,7 @@
   m <- go 0 schs0 $ runTransFusion productDecoder
   return $ evalContT $ do
     offsets <- decodeOffsets (length schs0)
-    asks $ \bs -> m offsets bs
+    lift $ m offsets
 
 -- | Generic implementation of 'schemaVia' for an ADT.
 gschemaViaVariant :: forall proxy a. (GSerialiseVariant (Rep a), Typeable a, Generic a) => proxy a -> [TypeRep] -> Schema
@@ -867,7 +878,7 @@
       | (name, sch) <- schs0]
     return $ evalContT $ do
       i <- decodeVarInt
-      lift $ fmap to $ ds' V.! i
+      lift $ fmap to $ maybe (throw InvalidTag) id $ ds' V.!? i
   s -> unexpectedSchema' rep "a variant" s
   where
     rep = "gdeserialiserVariant :: Deserialiser "
@@ -890,7 +901,7 @@
 instance (GSerialiseProduct f, Constructor c) => GSerialiseVariant (C1 c f) where
   variantCount _ = 1
   variantSchema _ ts = [(T.pack $ conName (M1 undefined :: M1 i c f x), productSchema (Proxy :: Proxy f) ts)]
-  variantEncoder i (M1 a) = encodeVarInt i <> encodeMulti (productEncoder a)
+  variantEncoder i (M1 a) = BB.varInt i <> encodeMulti (productEncoder a)
   variantDecoder = [(T.pack $ conName (M1 undefined :: M1 i c f x)
     , fmap (fmap M1) . deserialiserProduct') ]
 
diff --git a/src/Data/Winery/Internal.hs b/src/Data/Winery/Internal.hs
--- a/src/Data/Winery/Internal.hs
+++ b/src/Data/Winery/Internal.hs
@@ -12,13 +12,13 @@
   , EncodingMulti
   , encodeMulti
   , encodeItem
-  , encodeVarInt
   , Decoder
   , decodeAt
   , decodeVarInt
   , Offsets
   , decodeOffsets
   , getWord8
+  , DecodeException(..)
   , word16be
   , word32be
   , word64be
@@ -33,12 +33,14 @@
   )where
 
 import Control.Applicative
+import Control.Exception
 import Control.Monad
 import Control.Monad.Fix
 import Control.Monad.ST
 import Control.Monad.Trans.Cont
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Unsafe as B
+import qualified Data.ByteString.Internal as B
 import Data.Winery.Internal.Builder
 import Data.Bits
 import Data.Dynamic
@@ -48,34 +50,25 @@
 import qualified Data.Vector.Unboxed as U
 import qualified Data.Vector.Unboxed.Mutable as UM
 import Data.Word
+import Foreign.ForeignPtr
+import Foreign.Storable
+import System.Endian
 
 type Decoder = (->) B.ByteString
 
 decodeAt :: (Int, Int) -> Decoder a -> Decoder a
 decodeAt (i, l) m = m . B.take l . B.drop i
 
-encodeVarInt :: (Bits a, Integral a) => a -> Encoding
-encodeVarInt n
-  | n < 0 = case negate n of
-    n'
-      | n' < 0x40 -> word8 (fromIntegral n' `setBit` 6)
-      | otherwise -> encodesUVarInt (word8 (0xc0 .|. fromIntegral n')) (unsafeShiftR n' 6)
-  | n < 0x40 = word8 (fromIntegral n)
-  | otherwise = encodesUVarInt (word8 (fromIntegral n `setBit` 7 `clearBit` 6)) (unsafeShiftR n 6)
-{-# SPECIALISE encodeVarInt :: Int -> Encoding #-}
-
-encodesUVarInt :: (Bits a, Integral a) => Encoding -> a -> Encoding
-encodesUVarInt !acc m
-  | m < 0x80 = acc `mappend` word8 (fromIntegral m)
-  | otherwise = encodesUVarInt (acc <> word8 (setBit (fromIntegral m) 7)) (unsafeShiftR m 7)
-{-# SPECIALISE encodesUVarInt :: Encoding -> Int -> Encoding #-}
-
 getWord8 :: ContT r Decoder Word8
 getWord8 = ContT $ \k bs -> case B.uncons bs of
   Nothing -> k 0 bs
   Just (x, bs') -> k x $! bs'
 {-# INLINE getWord8 #-}
 
+data DecodeException = InsufficientInput
+  | InvalidTag deriving (Eq, Show)
+instance Exception DecodeException
+
 decodeVarInt :: (Num a, Bits a) => ContT r Decoder a
 decodeVarInt = getWord8 >>= \case
   n | testBit n 7 -> do
@@ -98,7 +91,7 @@
   then
     (fromIntegral (s `B.unsafeIndex` 0) `unsafeShiftL` 8) .|.
     (fromIntegral (s `B.unsafeIndex` 1))
-  else error "word16be"
+  else throw InsufficientInput
 
 word32be :: B.ByteString -> Word32
 word32be = \s -> if B.length s >= 4
@@ -107,20 +100,13 @@
     (fromIntegral (s `B.unsafeIndex` 1) `unsafeShiftL` 16) .|.
     (fromIntegral (s `B.unsafeIndex` 2) `unsafeShiftL`  8) .|.
     (fromIntegral (s `B.unsafeIndex` 3) )
-  else error "word32be"
+  else throw InsufficientInput
 
 word64be :: B.ByteString -> Word64
-word64be = \s -> if B.length s >= 8
-  then
-    (fromIntegral (s `B.unsafeIndex` 0) `unsafeShiftL` 56) .|.
-    (fromIntegral (s `B.unsafeIndex` 1) `unsafeShiftL` 48) .|.
-    (fromIntegral (s `B.unsafeIndex` 2) `unsafeShiftL` 40) .|.
-    (fromIntegral (s `B.unsafeIndex` 3) `unsafeShiftL` 32) .|.
-    (fromIntegral (s `B.unsafeIndex` 4) `unsafeShiftL` 24) .|.
-    (fromIntegral (s `B.unsafeIndex` 5) `unsafeShiftL` 16) .|.
-    (fromIntegral (s `B.unsafeIndex` 6) `unsafeShiftL`  8) .|.
-    (fromIntegral (s `B.unsafeIndex` 7) )
-  else error $ "word64be" ++ show s
+word64be (B.PS fp ofs len)
+  | len >= 8 = B.accursedUnutterablePerformIO $ withForeignPtr fp
+    $ \ptr -> fromBE64 <$> peekByteOff ptr ofs
+  | otherwise = throw InsufficientInput
 
 data EncodingMulti = EncodingMulti0
     | EncodingMulti !Encoding !Encoding
@@ -134,7 +120,7 @@
 encodeItem :: Encoding -> EncodingMulti -> EncodingMulti
 encodeItem e EncodingMulti0 = EncodingMulti mempty e
 encodeItem e (EncodingMulti a b) = EncodingMulti
-  (mappend (encodeVarInt (getSize e)) a) (mappend e b)
+  (mappend (varInt (getSize e)) a) (mappend e b)
 {-# INLINE encodeItem #-}
 
 type Offsets = U.Vector (Int, Int)
@@ -146,12 +132,12 @@
     r <- UM.unsafeNew (U.length xs + 1)
     let go s i
           | i == U.length xs = do
-            UM.write r i (s, maxBound)
+            UM.unsafeWrite r i (s, maxBound)
             U.unsafeFreeze r
           | otherwise = do
             let x = U.unsafeIndex xs i
             let s' = s + x
-            UM.write r i (s, x)
+            UM.unsafeWrite r i (s, x)
             go s' (i + 1)
     go 0 0
 
diff --git a/src/Data/Winery/Internal/Builder.hs b/src/Data/Winery/Internal/Builder.hs
--- a/src/Data/Winery/Internal/Builder.hs
+++ b/src/Data/Winery/Internal/Builder.hs
@@ -1,25 +1,34 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, BangPatterns #-}
+{-# LANGUAGE RecordWildCards #-}
 module Data.Winery.Internal.Builder
   ( Encoding
   , getSize
   , toByteString
+  , hPut
   , word8
   , word16
   , word32
   , word64
   , bytes
+  , varInt
   ) where
 
+import Data.Bits hiding (rotate)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Internal as B
 import Data.Word
 #if !MIN_VERSION_base(4,11,0)
 import Data.Semigroup
 #endif
+import Data.IORef
 import Foreign.Ptr
 import Foreign.ForeignPtr
 import Foreign.Storable
+import GHC.IO.Buffer
+import GHC.IO.Handle.Internals
+import GHC.IO.Handle.Types
+import qualified GHC.IO.BufferedIO as Buffered
 import System.IO.Unsafe
 import System.Endian
 
@@ -96,3 +105,66 @@
 bytes :: B.ByteString -> Encoding
 bytes bs = Encoding (B.length bs) $ LBytes bs
 {-# INLINE bytes #-}
+
+varInt :: (Bits a, Integral a) => a -> Encoding
+varInt n
+  | n < 0 = case negate n of
+    n'
+      | n' < 0x40 -> word8 (fromIntegral n' `setBit` 6)
+      | otherwise -> uvarInt 1 (LWord8 (0xc0 .|. fromIntegral n')) (unsafeShiftR n' 6)
+  | n < 0x40 = word8 (fromIntegral n)
+  | otherwise = uvarInt 1 (LWord8 (fromIntegral n `setBit` 7 `clearBit` 6)) (unsafeShiftR n 6)
+{-# SPECIALISE varInt :: Int -> Encoding #-}
+
+uvarInt :: (Bits a, Integral a) => Int -> Tree -> a -> Encoding
+uvarInt siz acc m
+  | m < 0x80 = Encoding (siz + 1) (acc `Bin` LWord8 (fromIntegral m))
+  | otherwise = uvarInt (siz + 1) (acc `Bin` LWord8 (setBit (fromIntegral m) 7)) (unsafeShiftR m 7)
+
+pokeBuffer :: (Buffered.BufferedIO dev, Storable a) => dev -> Buffer Word8 -> a -> IO (Buffer Word8)
+pokeBuffer dev buf x
+    | bufferAvailable buf >= sizeOf x = bufferAdd (sizeOf x) buf
+      <$ withBuffer buf (\ptr -> pokeByteOff ptr (bufR buf) x)
+    | otherwise = Buffered.flushWriteBuffer dev buf
+      >>= \buf' -> bufferAdd (sizeOf x) buf'
+        <$ withBuffer buf' (\ptr -> pokeByteOff ptr (bufR buf') x)
+{-# INLINE pokeBuffer #-}
+
+hPut :: Handle -> Encoding -> IO ()
+hPut _ Empty = return ()
+hPut h (Encoding _ t0) = wantWritableHandle "Data.Winery.Intenal.Builder.hPut" h
+  $ \Handle__{..} -> do
+    buf0 <- readIORef haByteBuffer
+
+    let go (LWord8 w) !buf = pokeBuffer haDevice buf w
+        go (LWord16 w) buf = pokeBuffer haDevice buf (toBE16 w)
+        go (LWord32 w) buf = pokeBuffer haDevice buf (toBE32 w)
+        go (LWord64 w) buf = pokeBuffer haDevice buf (toBE64 w)
+        go t@(LBytes (B.PS fp ofs len)) !buf
+          | bufferAvailable buf >= len = (bufferAdd len buf<$) $ withBuffer buf
+            $ \ptr -> withForeignPtr fp
+            $ \src -> B.memcpy (ptr `plusPtr` bufR buf) (src `plusPtr` ofs) len
+          | bufSize buf >= len = Buffered.flushWriteBuffer haDevice buf
+            >>= go t
+          | otherwise = newByteBuffer len WriteBuffer >>= go t
+        go (Bin c d) buf = rot c d buf
+
+        rot (LWord8 w) t !buf = pokeBuffer haDevice buf w >>= go t
+        rot (LWord16 w) t buf = pokeBuffer haDevice buf (toBE16 w) >>= go t
+        rot (LWord32 w) t buf = pokeBuffer haDevice buf (toBE32 w) >>= go t
+        rot (LWord64 w) t buf = pokeBuffer haDevice buf (toBE64 w) >>= go t
+        rot t@(LBytes (B.PS fp ofs len)) t' buf
+          | bufferAvailable buf >= len = do
+            withBuffer buf
+              $ \ptr -> withForeignPtr fp
+              $ \src -> B.memcpy (ptr `plusPtr` bufR buf) (src `plusPtr` ofs) len
+            go t' $ bufferAdd len buf
+          | bufSize buf >= len = Buffered.flushWriteBuffer haDevice buf
+            >>= rot t t'
+          | otherwise = do
+            _ <- Buffered.flushWriteBuffer haDevice buf
+            newByteBuffer len WriteBuffer >>= rot t t'
+        rot (Bin c d) t buf = rot c (Bin d t) buf
+
+    buf' <- go t0 buf0
+    writeIORef haByteBuffer buf'
diff --git a/src/Data/Winery/Query.hs b/src/Data/Winery/Query.hs
--- a/src/Data/Winery/Query.hs
+++ b/src/Data/Winery/Query.hs
@@ -36,16 +36,17 @@
 invalid = Query . const . Deserialiser . Plan . const . errorStrategy
 
 list :: Query a a
-list = Query $ \d -> concat <$> extractListWith d
+list = Query $ \d -> concat <$> extractListBy d
 
 range :: Int -> Int -> Query a a
-range i j = Query $ \d -> (\(n, f) -> concatMap f [mod i n..mod j n]) <$> extractArrayWith d
+range i j = Query $ \d -> (\(n, f) -> concatMap f [mod i n..mod j n])
+  <$> extractArrayBy d
 
 field :: Typeable a => T.Text -> Query a a
-field name = Query $ \d -> extractFieldWith d name
+field name = Query $ \d -> extractFieldBy d name
 
 con :: Typeable a => T.Text -> Query a a
-con name = Query $ \d -> maybe [] id <$> extractConstructorWith d name
+con name = Query $ \d -> maybe [] id <$> extractConstructorBy d name
 
 select :: Query a Bool -> Query a a
 select qp = Query $ \d -> Deserialiser $ Plan $ \sch -> do
diff --git a/src/Data/Winery/Term.hs b/src/Data/Winery/Term.hs
--- a/src/Data/Winery/Term.hs
+++ b/src/Data/Winery/Term.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE OverloadedStrings, LambdaCase, ScopedTypeVariables #-}
 module Data.Winery.Term where
 
+import Data.Aeson
 import Control.Monad.Trans.Cont
 import Control.Monad.Reader
 import qualified Data.ByteString as B
@@ -12,6 +13,7 @@
 import Data.Winery.Internal
 import Data.Word
 import qualified Data.Vector.Unboxed as V
+import qualified Data.HashMap.Strict as HM
 
 -- | Common representation for any winery data.
 -- Handy for prettyprinting winery-serialised data.
@@ -37,6 +39,33 @@
   | TVariant !T.Text [Term]
   deriving Show
 
+instance ToJSON Term where
+  toJSON TUnit = toJSON ()
+  toJSON (TBool b) = toJSON b
+  toJSON (TChar c) = toJSON c
+  toJSON (TWord8 w) = toJSON w
+  toJSON (TWord16 w) = toJSON w
+  toJSON (TWord32 w) = toJSON w
+  toJSON (TWord64 w) = toJSON w
+  toJSON (TInt8 w) = toJSON w
+  toJSON (TInt16 w) = toJSON w
+  toJSON (TInt32 w) = toJSON w
+  toJSON (TInt64 w) = toJSON w
+  toJSON (TInteger w) = toJSON w
+  toJSON (TFloat x) = toJSON x
+  toJSON (TDouble x) = toJSON x
+  toJSON (TBytes bs) = toJSON (B.unpack bs)
+  toJSON (TText t) = toJSON t
+  toJSON (TList xs) = toJSON xs
+  toJSON (TProduct xs) = toJSON xs
+  toJSON (TRecord xs) = toJSON $ HM.fromList xs
+  toJSON (TVariant "Just" [x]) = toJSON x
+  toJSON (TVariant "Nothing" []) = Null
+  toJSON (TVariant t []) = toJSON t
+  toJSON (TVariant t [x]) = object ["tag" .= toJSON t, "contents" .= toJSON x]
+  toJSON (TVariant t xs) = object ["tag" .= toJSON t, "contents" .= toJSON xs]
+
+
 -- | Deserialiser for a 'Term'.
 decodeTerm :: Deserialiser Term
 decodeTerm = go [] where
@@ -58,8 +87,8 @@
     SDouble -> p s TDouble
     SBytes -> p s TBytes
     Data.Winery.SText -> p s TText
-    SArray siz sch -> fmap TList <$> extractListWith (go points) `unwrapDeserialiser` SArray siz sch
-    SList sch -> fmap TList <$> extractListWith (go points) `unwrapDeserialiser` SList sch
+    SArray siz sch -> fmap TList <$> extractListBy (go points) `unwrapDeserialiser` SArray siz sch
+    SList sch -> fmap TList <$> extractListBy (go points) `unwrapDeserialiser` SList sch
     SProduct schs -> do
       decoders <- traverse (unwrapDeserialiser $ go points) schs
       return $ evalContT $ do
diff --git a/winery.cabal b/winery.cabal
--- a/winery.cabal
+++ b/winery.cabal
@@ -1,11 +1,11 @@
--- This file has been generated from package.yaml by hpack version 0.20.0.
+-- This file has been generated from package.yaml by hpack version 0.28.2.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: f441dfe5896bdc24f2600a35982ae2f08288dc6b07958d380edeabb5e816dfbc
+-- hash: ef83aa777baa5f4b7fcf4c9708a493c8fbd187e078f3eef8d6ea02d8e257fcd9
 
 name:           winery
-version:        0.1.2
+version:        0.2
 synopsis:       Sustainable serialisation library
 description:    Please see the README on Github at <https://github.com/fumieval/winery#readme>
 category:       Data
@@ -13,12 +13,11 @@
 bug-reports:    https://github.com/fumieval/winery/issues
 author:         Fumiaki Kinoshita
 maintainer:     fumiexcel@gmail.com
-copyright:      Copyright (c) 2017 Fumiaki Kinoshita
+copyright:      Copyright (c) 2018 Fumiaki Kinoshita
 license:        BSD3
 license-file:   LICENSE
 build-type:     Simple
 cabal-version:  >= 1.10
-
 extra-source-files:
     ChangeLog.md
     README.md
@@ -32,7 +31,8 @@
       src
   ghc-options: -Wall -O2
   build-depends:
-      base >=4.7 && <5
+      aeson
+    , base >=4.7 && <5
     , bytestring
     , containers
     , cpu
@@ -50,19 +50,22 @@
       Data.Winery
       Data.Winery.Term
       Data.Winery.Internal
+      Data.Winery.Internal.Builder
       Data.Winery.Query
       Data.Winery.Query.Parser
   other-modules:
-      Data.Winery.Internal.Builder
       Paths_winery
   default-language: Haskell2010
 
 executable winery
   main-is: Main.hs
+  other-modules:
+      Paths_winery
   hs-source-dirs:
       app
   build-depends:
-      base >=4.7 && <5
+      aeson
+    , base >=4.7 && <5
     , bytestring
     , containers
     , cpu
@@ -77,8 +80,6 @@
     , unordered-containers
     , vector
     , winery
-  other-modules:
-      Paths_winery
   default-language: Haskell2010
 
 test-suite spec
@@ -87,7 +88,8 @@
   hs-source-dirs:
       test
   build-depends:
-      base >=4.7 && <5
+      aeson
+    , base >=4.7 && <5
     , bytestring
     , containers
     , cpu
@@ -109,17 +111,21 @@
 benchmark bench-winery
   type: exitcode-stdio-1.0
   main-is: bench.hs
+  other-modules:
+      Paths_winery
   hs-source-dirs:
       benchmarks
   ghc-options: -O2
   build-depends:
-      base >=4.7 && <5
+      aeson
+    , base >=4.7 && <5
     , binary
     , bytestring
     , cassava
     , containers
     , cpu
     , deepseq
+    , directory
     , gauge
     , hashable
     , megaparsec
@@ -133,6 +139,4 @@
     , unordered-containers
     , vector
     , winery
-  other-modules:
-      Paths_winery
   default-language: Haskell2010
