Reputation: 16059
I'm creating a JSON REST API (lots of caps there), and already have Data.Aeson.Generic working nicely. In the following, serializedString would be {"x":10, "y":10}
import qualified Data.Aeson.Generic as A
import Data.Data (Data, Typeable)
data Unit = Unit { x :: Int, y :: Int } deriving (Show, Eq, Data, Typeable)
example = do
let serializedByteString = A.encode (Unit 10 10)
I would like to have my api respond like this for successes:
{unit:{x:10, y:10}}
And this for failures
{error:"Didn't work!"}
I was thinking of making some kind of Response
data type, with Response
and Error
constructors. It's easy to serialize Error
, but response could have all kinds of different objects, and rather than send back {data:{...}}
I'd like to do {unit:{...}}
.
Is there a way to define my Response
value constructor so that it works with anything deriving Data
? Is there a way to know what the name of the value constructor is when I go to serialize my object? show
knows it somehow. Thanks!
Upvotes: 2
Views: 622
Reputation: 16059
Doesn't work for the Error constructor yet (I think), but this works for unit
{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
module Types where
import Data.Data (Data, Typeable, typeOf)
import Data.Aeson (ToJSON(..), (.=), object)
import qualified Data.Aeson.Generic as AG
import qualified Data.Text as T
import Data.Char (toLower)
data Unit = Unit { x :: Int, y :: Int } deriving (Show, Data, Typeable)
data Message a = Message { obj :: a } |
Error String
deriving (Show, Data, Typeable)
instance (Data a, Typeable a) => ToJSON (Message a) where
toJSON m = object [T.pack typeName .= AG.toJSON (obj m)]
where o = obj m
typeName = map toLower $ show $ typeOf o
You have to use Data.Aeson.encode, rather than Data.Aeson.Generic.encode on the top-level message. The key concept I was missing was typeOf from Data.Data.Typeable. It gives you a string representing the data constructor. It will be interesting to try to go the other direction using FromJSON
Edit: to be clear, you can then call Data.Aeson.encode $ Message $ Unit 10 10
and get {unit:{x:10, y:10}}
Upvotes: 1