diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016, Kei Hibino
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Kei Hibino nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/json-rpc-generic.cabal b/json-rpc-generic.cabal
new file mode 100644
--- /dev/null
+++ b/json-rpc-generic.cabal
@@ -0,0 +1,77 @@
+name:                json-rpc-generic
+version:             0.0.1.0
+synopsis:            Generic encoder and decode for JSON-RPC
+description:         This package contains generic encoder and decode for JSON-RPC
+homepage:            http://github.com/khibino/haskell-json-rpc-generic
+license:             BSD3
+license-file:        LICENSE
+author:              Kei Hibino
+maintainer:          ex8k.hibino@gmail.com
+copyright:           Copyright (c) 2016 Kei Hibino
+category:            Network
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+tested-with:           GHC == 8.0.1
+                     , GHC == 7.10.1, GHC == 7.10.2, GHC == 7.10.3
+                     , GHC == 7.8.1, GHC == 7.8.2, GHC == 7.8.3, GHC == 7.8.4
+                     , GHC == 7.6.1, GHC == 7.6.2, GHC == 7.6.3
+
+library
+  exposed-modules:
+                       Data.JsonRpc.Id
+                       Data.JsonRpc.Request
+                       Data.JsonRpc.Success
+                       Data.JsonRpc.Failure
+                       Data.JsonRpc.Response
+                       Data.JsonRpc.Generic
+                       Data.JsonRpc.Instances
+
+                       Data.JsonRpc
+  -- other-modules:
+  other-extensions:
+                       TypeOperators
+                       FlexibleContexts
+                       OverloadedStrings
+                       DeriveFunctor
+                       DeriveFoldable
+                       DeriveTraversable
+                       -- DeriveGeneric  -- compat with Cabal 1.16
+                       StandaloneDeriving
+
+  build-depends:         base >=4.6 && <5
+                       , transformers
+                       , bytestring >=0.10
+                       , text
+                       , scientific
+                       , vector >=0.10
+                       , aeson >=0.7
+
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+
+test-suite test-main
+  main-is:             testMain.hs
+  other-modules:
+                       Instances
+                       Iso
+                       Eq
+  build-depends:         base <5
+                       , aeson
+                       , text
+                       , json-rpc-generic
+                       , quickcheck-simple
+                       , QuickCheck
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+source-repository head
+  type:       git
+  location:   https://github.com/khibino/haskell-json-rpc-generic
+
+source-repository head
+  type:       mercurial
+  location:   https://bitbucket.org/khibino/haskell-json-rpc-generic
diff --git a/src/Data/JsonRpc.hs b/src/Data/JsonRpc.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc.hs
@@ -0,0 +1,16 @@
+module Data.JsonRpc (
+  module Data.JsonRpc.Id,
+  module Data.JsonRpc.Request,
+  module Data.JsonRpc.Success,
+  module Data.JsonRpc.Failure,
+  module Data.JsonRpc.Response,
+  module Data.JsonRpc.Generic,
+  ) where
+
+import Data.JsonRpc.Id
+import Data.JsonRpc.Request
+import Data.JsonRpc.Success
+import Data.JsonRpc.Failure hiding (_jsonrpc, _id)
+import Data.JsonRpc.Response
+import Data.JsonRpc.Generic
+import Data.JsonRpc.Instances ()
diff --git a/src/Data/JsonRpc/Failure.hs b/src/Data/JsonRpc/Failure.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Failure.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
+
+module Data.JsonRpc.Failure (
+  Failure (..), Error (..), ErrorStatus (..),
+
+  failure, makeError,
+  serverError,
+  methodError,
+  emptyError,
+  ) where
+
+import Prelude hiding (userError)
+import Control.Monad (MonadPlus, guard)
+import Data.Text (Text)
+import Data.Foldable (Foldable)
+import Data.Traversable (Traversable)
+
+import Data.JsonRpc.Id (Id)
+
+
+data Failure e =
+  Failure
+  { _jsonrpc :: !Text
+  , _id      :: !(Maybe Id)
+  , _error   :: !(Error e)
+  } deriving (Eq, Show, Functor, Foldable, Traversable)
+
+data Error e =
+  Error
+  { _code    :: !Int
+  , _message :: !Text
+  , _data    :: !(Maybe e)
+  } deriving (Eq, Show, Functor, Foldable, Traversable)
+
+data ErrorStatus
+  = ParseError
+  | InvalidRequest
+  | MethodNotFound
+  | InvalidParams
+  | InternalError
+  | ServerError !Int
+  | MethodError !Int !Text
+  deriving (Eq, Show)
+
+failure :: Maybe Id -> ErrorStatus -> Maybe e -> Failure e
+failure mayId s =
+  Failure "2.0" mayId . makeError s
+
+makeError :: ErrorStatus -> Maybe e -> Error e
+makeError = d  where
+  d  ParseError         =  Error (-32700) "Parse error"
+  d  InvalidRequest     =  Error (-32600) "Invalid Request"
+  d  MethodNotFound     =  Error (-32601) "Method not found"
+  d  InvalidParams      =  Error (-32602) "Invalid params"
+  d  InternalError      =  Error (-32603) "Internal error"
+  d (ServerError c)     =  Error       c  "Server error"
+  d (MethodError c m)   =  Error       c   m
+
+serverError :: MonadPlus m
+            => Int
+            -> m ErrorStatus
+serverError c = do
+  guard $ -32099 <= c && c <= -32000
+  return $ ServerError c
+
+methodError :: MonadPlus m
+            => Int
+            -> Text
+            -> m ErrorStatus
+methodError c s = do
+  guard $ c < -32768 || -32000 < c
+  return $ MethodError c s
+
+emptyError :: Maybe ()
+emptyError = Nothing
diff --git a/src/Data/JsonRpc/Generic.hs b/src/Data/JsonRpc/Generic.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Generic.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Data.JsonRpc.Generic (
+  GFromArrayJSON, genericParseJSONRPC,
+
+  GToArrayJSON, genericToArrayJSON,
+  ) where
+
+import GHC.Generics
+import Control.Applicative ((<$>), (<*>), (<*), empty)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.State (StateT, evalStateT, get, put)
+import Data.Aeson.Types
+  (FromJSON (..), ToJSON (..), GFromJSON, genericParseJSON, Parser, Options, Value (..))
+import Data.Vector (Vector)
+import qualified Data.Vector as Vector
+
+
+class GFromArrayJSON f where
+  gFromArrayJSON :: StateT [Value] Parser (f a)
+
+instance GFromArrayJSON U1 where
+  gFromArrayJSON  =  return U1
+
+instance (GFromArrayJSON a, GFromArrayJSON b) => GFromArrayJSON (a :*: b) where
+  gFromArrayJSON  =  (:*:) <$> gFromArrayJSON <*> gFromArrayJSON
+
+instance GFromArrayJSON a => GFromArrayJSON (M1 i c a) where
+  gFromArrayJSON  =  M1 <$> gFromArrayJSON
+
+instance FromJSON a => GFromArrayJSON (K1 i a) where
+  gFromArrayJSON  =  do
+    vs'  <-  get
+    K1 <$> case vs' of
+     v:vs  ->  (lift $ parseJSON v)   <* put vs
+     []    ->   lift $ parseJSON Null
+
+
+genericParseJSONRPC :: (Generic a, GFromJSON (Rep a), GFromArrayJSON (Rep a))
+                    => Options -> Value -> Parser a
+genericParseJSONRPC opt = d where
+  d (Array vs)      =  (to <$>) . evalStateT gFromArrayJSON $ Vector.toList vs
+                       -- check state to check too many arguments
+  d v@(Object _)    =  genericParseJSON opt v
+  d _               =  empty
+
+
+class GToArrayJSON f where
+  gToArrayJSON :: f a -> Vector Value
+
+instance GToArrayJSON U1 where
+  gToArrayJSON U1 = Vector.empty
+
+instance (GToArrayJSON a, GToArrayJSON b) => GToArrayJSON (a :*: b) where
+  gToArrayJSON (x :*: y)  =  gToArrayJSON x Vector.++ gToArrayJSON y
+
+instance GToArrayJSON a => GToArrayJSON (M1 i c a) where
+  gToArrayJSON (M1 x)  =  gToArrayJSON x
+
+instance ToJSON a => GToArrayJSON (K1 i a) where
+  gToArrayJSON (K1 x)  =  Vector.singleton $ toJSON x
+
+genericToArrayJSON :: (Generic a, GToArrayJSON (Rep a))
+                   => a -> Value
+genericToArrayJSON = Array . gToArrayJSON . from
diff --git a/src/Data/JsonRpc/Id.hs b/src/Data/JsonRpc/Id.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Id.hs
@@ -0,0 +1,41 @@
+
+module Data.JsonRpc.Id (Id(..), numberId) where
+
+import Control.Applicative (Applicative, pure)
+import Control.Monad (MonadPlus, guard)
+
+import Data.Text (Text)
+import Data.Scientific (Scientific, toDecimalDigits)
+
+
+{-
+-- citation from http://www.jsonrpc.org/specification
+--
+--  An identifier established by the Client that MUST contain
+--  a String, Number, or NULL value if included.
+--  If it is not included it is assumed to be a notification.
+--  The value SHOULD normally not be Null [1]
+--  and Numbers SHOULD NOT contain fractional parts [2]
+--
+--  [1] The use of Null as a value for the id member in
+--  a Request object is discouraged, because this specification
+--  uses a value of Null for Responses with an unknown id.
+--  Also, because JSON-RPC 1.0 uses an id value of Null
+--  for Notifications this could cause confusion in handling.
+
+--  [2] Fractional parts may be problematic,
+--  since many decimal fractions cannot be
+--  represented exactly as binary fractions.
+-}
+
+data Id
+  = StringId !Text
+  | NumberId !Integer
+  deriving (Eq, Ord, Show, Read)
+
+numberId :: (MonadPlus m, Applicative m) => Scientific -> m Id
+numberId sci = do
+  let (ds, e) = toDecimalDigits sci
+  guard $ sci == 0 || null (drop e ds)  -- test integral
+
+  pure . NumberId $ round sci
diff --git a/src/Data/JsonRpc/Instances.hs b/src/Data/JsonRpc/Instances.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Instances.hs
@@ -0,0 +1,80 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module Data.JsonRpc.Instances () where
+
+import GHC.Generics (Generic)
+import Control.Applicative ((<$>), pure, (<|>))
+import Data.Maybe (fromMaybe)
+import Data.List (stripPrefix)
+import Data.Aeson (FromJSON (..), genericParseJSON, ToJSON (..), genericToJSON, Value(..))
+import Data.Aeson.Types (Options (..))
+import qualified Data.Aeson.Types as Aeson
+
+import Data.JsonRpc.Id (Id(..), numberId)
+import Data.JsonRpc.Request (Request (..))
+import Data.JsonRpc.Success (Success (..))
+import Data.JsonRpc.Failure (Failure (..), Error (..))
+import Data.JsonRpc.Response (Response (..))
+
+
+instance FromJSON Id where
+  parseJSON = d  where
+    d (String t)   =  pure $ StringId t
+    d (Number n)   =  maybe (parseError "Integer check error") pure $ numberId n
+    d (Object {})  =  parseError "object is not allowed"
+    d (Array  {})  =  parseError "array is not allowed"
+    d (Bool   {})  =  parseError "boolean is not allowed"
+    d  Null        =  parseError "null is not allowed"
+    parseError = fail . ("JSON RPC id: " ++)
+
+instance ToJSON Id where
+  toJSON = d  where
+    d (StringId s)  =  String s
+    d (NumberId i)  =  Number $ fromIntegral i
+
+deriving instance Generic (Request a)
+
+instance FromJSON a => FromJSON (Request a) where
+  parseJSON = genericParseJSON customOptions
+
+instance ToJSON a => ToJSON (Request a) where
+  toJSON = genericToJSON customOptions
+
+deriving instance Generic (Success a)
+
+instance FromJSON a => FromJSON (Success a) where
+  parseJSON = genericParseJSON customOptions
+
+instance ToJSON a => ToJSON (Success a) where
+  toJSON = genericToJSON customOptions
+
+deriving instance Generic (Error e)
+deriving instance Generic (Failure e)
+
+instance FromJSON e => FromJSON (Error e) where
+  parseJSON = genericParseJSON customOptions
+
+instance ToJSON e => ToJSON (Error e) where
+  toJSON = genericToJSON customOptions { omitNothingFields  =  True }
+
+instance FromJSON e => FromJSON (Failure e) where
+  parseJSON = genericParseJSON customOptions
+
+instance ToJSON e => ToJSON (Failure e) where
+  toJSON = genericToJSON customOptions
+
+instance (FromJSON e, FromJSON a) => FromJSON (Response e a) where
+  parseJSON v = Response <$> (Right <$> parseJSON v <|>
+                              Left  <$> parseJSON v)
+
+instance (ToJSON e, ToJSON a) => ToJSON (Response e a) where
+  toJSON (Response r) = case r of
+    Right a  -> toJSON a
+    Left  e  -> toJSON e
+
+customOptions :: Options
+customOptions =
+  Aeson.defaultOptions
+  { fieldLabelModifier  =  \s -> fromMaybe s $ stripPrefix "_" s }
diff --git a/src/Data/JsonRpc/Request.hs b/src/Data/JsonRpc/Request.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Request.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
+
+module Data.JsonRpc.Request (
+  Request(..), request,
+  ) where
+
+import Prelude hiding (id)
+import Data.Text (Text, pack)
+import Data.Foldable (Foldable)
+import Data.Traversable (Traversable)
+
+import Data.JsonRpc.Id (Id)
+
+
+data Request a =
+  Request
+  { jsonrpc :: !Text
+  , method  :: !Text
+  , params  :: !(Maybe a)
+  , id      :: !(Maybe Id)
+  } deriving (Eq, Show, Read, Functor, Foldable, Traversable)
+
+request :: Text -> Maybe a -> Maybe Id -> Request a
+request = Request $ pack "2.0"
diff --git a/src/Data/JsonRpc/Response.hs b/src/Data/JsonRpc/Response.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Response.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
+module Data.JsonRpc.Response (
+  Response (..),
+  ) where
+
+import Data.JsonRpc.Success (Success (..))
+import Data.JsonRpc.Failure (Failure (..))
+
+
+newtype Response e a =
+  Response (Either (Failure e) (Success a))
+  deriving (Eq, Show, Functor)
diff --git a/src/Data/JsonRpc/Success.hs b/src/Data/JsonRpc/Success.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JsonRpc/Success.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
+
+module Data.JsonRpc.Success (
+  Success (..), success,
+  ) where
+
+import Data.Text (Text, pack)
+import Data.Foldable (Foldable)
+import Data.Traversable (Traversable)
+
+import Data.JsonRpc.Id (Id)
+
+
+data Success a =
+  Success
+  { _jsonrpc :: !Text
+  , _id      :: !Id
+  , _result  :: !a
+  } deriving (Eq, Show, Functor, Foldable, Traversable)
+
+success :: Id -> a -> Success a
+success = Success $ pack "2.0"
diff --git a/test/Eq.hs b/test/Eq.hs
new file mode 100644
--- /dev/null
+++ b/test/Eq.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveGeneric #-}
+
+module Eq (tests) where
+
+import Test.QuickCheck.Simple (Test, boolTest)
+
+import GHC.Generics (Generic)
+
+import Prelude hiding (id)
+import Data.Maybe (fromMaybe)
+import Data.Aeson (FromJSON)
+import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Types as Aeson
+
+import Data.JsonRpc
+  (Id(..), numberId, Request (..),
+   Success, success, Failure, failure, Response (..),
+  genericParseJSONRPC)
+import qualified Data.JsonRpc.Failure as Failure
+
+
+data Foo =
+  Foo
+  { foo :: Int
+  , bar :: String
+  , baz :: [Int]
+  } deriving (Eq, Show, Generic)
+
+instance FromJSON Foo where
+  parseJSON = genericParseJSONRPC Aeson.defaultOptions
+
+eqDecode0 :: Bool
+eqDecode0 =
+  Aeson.decode
+  "{\"jsonrpc\": \"2.0\", \"method\": \"foo\", \"params\": { \"bar\": \"Hello\", \"foo\": 234, \"baz\": [5, 6, 7, 8] }, \"id\": 3}"
+  ==
+  Just (Request { jsonrpc = "2.0"
+                , method = "foo"
+                , params = Just (Foo {foo = 234, bar = "Hello", baz = [5,6,7,8]})
+                , id = Just (NumberId 3)}
+       )
+
+eqDecode1 :: Bool
+eqDecode1 =
+  Aeson.decode
+  "{\"jsonrpc\": \"2.0\", \"method\": \"foo\", \"params\": [ 234, \"Hello\", [5, 6, 7, 8] ], \"id\": 3}"
+  ==
+  Just (Request { jsonrpc = "2.0"
+                , method = "foo"
+                , params = Just (Foo {foo = 234, bar = "Hello", baz = [5,6,7,8]})
+                , id = Just (NumberId 3)}
+       )
+
+exId :: Id
+exId = fromMaybe (error "something wrong: _success") $ numberId 25
+
+exSuccess :: Success Foo
+exSuccess = success exId Foo {foo = 234, bar = "Hello", baz = [5,6,7,8]}
+
+exFailure :: Failure String
+exFailure = failure (Just exId) Failure.InvalidRequest Nothing
+
+eqResponseS :: Bool
+eqResponseS =
+  Just (Response $ Right exSuccess :: Response String Foo)
+  ==
+  Aeson.decode "{\"result\":{ \"bar\": \"Hello\", \"foo\": 234, \"baz\": [5, 6, 7, 8] },\"jsonrpc\":\"2.0\",\"id\":25}"
+
+eqResponseF :: Bool
+eqResponseF =
+  Just (Response $ Left exFailure :: Response String Foo)
+  ==
+  Aeson.decode "{\"error\":{\"code\":-32600,\"message\":\"Invalid Request\"},\"jsonrpc\":\"2.0\",\"id\":25}"
+
+tests :: [Test]
+tests =
+  [ boolTest "eq - decode 0" eqDecode0
+  , boolTest "eq - decode 1" eqDecode1
+  , boolTest "eq - response success" eqResponseS
+  , boolTest "eq - response failure" eqResponseF
+  ]
diff --git a/test/Instances.hs b/test/Instances.hs
new file mode 100644
--- /dev/null
+++ b/test/Instances.hs
@@ -0,0 +1,108 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE DeriveGeneric #-}
+
+module Instances (Example (..)) where
+
+import Test.QuickCheck (Arbitrary (..), Gen, frequency, choose)
+
+import GHC.Generics (Generic)
+import Control.Applicative ((<$>), pure, (<*>))
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Aeson (FromJSON, ToJSON)
+import qualified Data.Aeson.Types as Aeson
+
+import Data.JsonRpc
+  (Id(..), Request (..), request,
+   Success (..), success,
+   Failure (..), Error (..), ErrorStatus (..), failure, makeError,
+   Response (..),
+   genericParseJSONRPC, )
+
+
+genText :: Gen Text
+genText = T.pack <$> arbitrary
+
+instance Arbitrary Id where
+  arbitrary =
+    frequency
+    [ (3, NumberId <$> arbitrary)
+    , (2, StringId . T.pack <$> arbitrary)
+    ]
+
+instance Arbitrary a => Arbitrary (Request a) where
+  arbitrary =
+    request
+    <$> genText
+    <*> arbitrary
+    <*> arbitrary
+
+instance Arbitrary a => Arbitrary (Success a) where
+  arbitrary =
+    success
+    <$> arbitrary
+    <*> arbitrary
+
+genServerError :: Gen ErrorStatus
+genServerError = ServerError <$> choose (-32099, -32000)
+
+genMethodErrorA :: Gen ErrorStatus
+genMethodErrorA = MethodError <$> choose (-31999, 0) <*> genText
+
+genMethodErrorB :: Gen ErrorStatus
+genMethodErrorB = MethodError <$> choose (-65535, -32769) <*> genText
+
+instance Arbitrary ErrorStatus where
+  arbitrary =
+    frequency
+    [ (1, pure ParseError)
+    , (1, pure InvalidRequest)
+    , (1, pure MethodNotFound)
+    , (1, pure InvalidParams)
+    , (1, pure InternalError)
+    , (2, genServerError)
+    , (2, genMethodErrorA)
+    , (2, genMethodErrorB)
+    ]
+
+instance Arbitrary e => Arbitrary (Error e) where
+  arbitrary =
+    makeError
+    <$> arbitrary
+    <*> arbitrary
+
+instance Arbitrary e => Arbitrary (Failure e) where
+  arbitrary =
+    failure
+    <$> arbitrary
+    <*> arbitrary
+    <*> arbitrary
+
+instance (Arbitrary e, Arbitrary a) => Arbitrary (Response e a) where
+  arbitrary =
+    frequency
+    [ (2, Response . Right <$> arbitrary)
+    , (3, Response . Left  <$> arbitrary)
+    ]
+
+
+data Example =
+  Example
+  { p :: Int
+  , q :: String
+  , r :: Maybe Int
+  , s :: [Int]
+  , t :: Maybe [Int]
+  , u :: Maybe String
+  } deriving (Eq, Show, Generic)
+
+instance Arbitrary Example where
+  arbitrary =
+    Example
+    <$> arbitrary <*> arbitrary <*> arbitrary
+    <*> arbitrary <*> arbitrary <*> arbitrary
+
+instance FromJSON Example where
+  parseJSON = genericParseJSONRPC Aeson.defaultOptions
+
+instance ToJSON Example
diff --git a/test/Iso.hs b/test/Iso.hs
new file mode 100644
--- /dev/null
+++ b/test/Iso.hs
@@ -0,0 +1,36 @@
+module Iso (tests) where
+
+import Test.QuickCheck.Simple (Test, qcTest)
+
+import Control.Applicative ((<$>))
+import Data.Aeson (FromJSON, ToJSON)
+import qualified Data.Aeson as Aeson
+
+import Data.JsonRpc (Request, Response, Error, genericToArrayJSON)
+
+import Instances (Example)
+
+
+aesonED :: (Eq a, ToJSON a, FromJSON a)
+        => a -> Bool
+aesonED x = Aeson.decode (Aeson.encode x) == Just x
+
+request :: Request Example -> Bool
+request = aesonED
+
+requestA :: Request Example -> Bool
+requestA r =
+  Aeson.decode (Aeson.encode $ genericToArrayJSON <$> r) == Just r
+
+errorObj :: Error Example -> Bool
+errorObj = aesonED
+
+response :: Response Example Example -> Bool
+response = aesonED
+
+tests :: [Test]
+tests =
+  [ qcTest "iso - request" request
+  , qcTest "iso - request array" requestA
+  , qcTest "iso - error object" errorObj
+  , qcTest "iso - response" response ]
diff --git a/test/testMain.hs b/test/testMain.hs
new file mode 100644
--- /dev/null
+++ b/test/testMain.hs
@@ -0,0 +1,7 @@
+
+import qualified Eq
+import qualified Iso
+import Test.QuickCheck.Simple (defaultMain)
+
+main :: IO ()
+main = defaultMain $ Eq.tests ++ Iso.tests
