Let's say I have a type Person
import GHC.Generics
import Data.Text
import Data.Aeson
import Control.Lens
data Person = Person {
_firstName :: Text,
_lastName :: Text,
_age :: Int
} deriving (Show, Generic)
And I want to automatically derive Lenses and JSON typeclasses for it
makeLenses ''Person
instance FromJSON Person
instance ToJSON Person
This works correctly, however DeriveGeneric sees my field names as having an underscore and expects my JSON to be formatted accordingly.
{ "_firstName": "James" ... etc} -- The underscore doesn't belong here.
Obviously I could remove the underscore from the data definition itself, but then makeLenses wouldn't be able to derive the required getters and setters.
Ideally what I want to be able to do is something like this
let person = decode blob
let name = person ^. firstName
i.e. I want to be able to derive lenses and JSON instances with all field names lining up correctly with the values in the JSON-REST Api I'm consuming, without having to write much boilerplate.
This seems like such a straight forward thing that I feel I'm missing something obvious?
Both lens and aeson have functions to allow customizable handling of field and constructor names. Since aeson's default is not what you want, and wouldn't work anyway if you want the lens names to be the same as the JSON field names, let's change the aeson configuration:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TemplateHaskell #-}
import GHC.Generics
import Data.Text hiding (drop)
import Data.Aeson
import Data.Aeson.TH
import Data.Aeson.Types
import Control.Lens
data Person = Person {
_firstName :: Text,
_lastName :: Text,
_age :: Int
} deriving (Show, Generic)
makeLenses ''Person
deriveJSON defaultOptions{fieldLabelModifier = drop 1} ''Person
{- alternative Generic version
instance FromJSON Person where
parseJSON = genericParseJSON defaultOptions{fieldLabelModifier = drop 1}
instance ToJSON Person where
toJSON = genericToJSON defaultOptions{fieldLabelModifier = drop 1}
-}
For lens, the corresponding configurable function would be makeLensesWith.
Related
I have a data:
data MyData = MyData { a :: String, b :: Integer, c :: Bool }
deriving (Generic)
instance FromJSON MyData
instance ToJSON MyData
In fact, I have many more fields in MyData.
I want to parse 1 or 2 fields manually because in MyData they're called slightly different than in the real JSON object, while still being able to have FromJSON and ToJSON or something like that. Is it possible? Or should I in this case parse all the fields manually and not use FromJSON / ToJSON?
You'll want to take a look at the template Haskell deriving abilities of aeson. There is an option there which helps you rename fields. For example, say I want to rename the color field to colour in the declaration below:
data MyData = MyData { address :: String
, streetNumber :: Integer
, isApartment :: Bool
, color :: String
}
Then, instead of deriving Generic, I add the following
{-# LANGUAGE TemplateHaskell #-}
import Data.Aeson.TH
data MyData = MyData { address :: String
, streetNumber :: Integer
, isApartment :: Bool
, color :: String
}
$(deriveJSON defaultOptions{
constructorTagModifier = \f -> if f == "color" then "colour" else f
} ''MyData)
Then my ToJSON and FromJSON instances have appropriately named fields.
In Aeson library meant for object serializing/deserializing, I see the functions, FromJSON & ToJSON declared as instances. The code is,
data Coord = Coord { x :: Double, y :: Double }
deriving (Show)
instance ToJSON Coord where
toJSON (Coord xV yV) = object [ "x" .= xV,
"y" .= yV ]
My questions are,
Why does the author create ToJSON/FromJSON instances with just one method? Can't toJSON/parseJSON be written as a function on its own?
In Python, one just does json.loads/json.dumps to handle any kind of object/json-string. Why does the haskell user need to write all these extra code for every object that he seralizes?
For composite objects with multiple hierarchies like
{"a":
{"b":
{
"c":1
}
}
}
, do we need to create multiple data and instance at each level?
Why does the author create ToJSON/FromJSON instances with just one method? Can't toJSON/parseJSON be written as a function on its own?
You misunderstand a lot of things, so let me clear this up a little. ToJSON and FromJSON aren't functions. These are typeclasses. Typeclasses are a way to write polymorphic code in Haskell.
Here I would explain a very simplified and incomplete definition of json serialization. First of all we declare a typeclass:
class ToJSON a where
toJSON :: a -> String
This statement basically says: "If a is an instance of typeclass ToJSON, then we can use function toJSON to serialize a into a JSON string".
When a typeclass is defined, one can implement instances of it for a variety of simple types:
instance ToJSON String where
toJSON s = s
instance ToJSON Int where
toJSON n = show n
instance ToJSON Double where
toJSON n = show n
After you defined these simple implementation, you can apply toJSON to values of either String, Int or Double and it would get dispatched to a right implementation:
toJSON "hello" -----> "hello"
toJSON (5 :: Int) -----> "5"
toJSON (5.5 :: Double) -----> "5.5"
To go further we need a way to encode JSON collections. Let's start with lists. We want to express that if there is a value a that can be serialized into JSON, then a list of such values can also be serialized into JSON.
-- ,-- value 'a' can be serialized into JSON
-- ,--------,
instance (ToJSON a) => ToJSON [a] where
-- ``````````-- A list of such values can also be serialized
-- | Here is how serialization can be performed
toJSON as = "[" ++ (intercalate ", " $ map toJSON as) ++ "]"
We serialize each value in the list, separate them with ", " and enclose in brackets. Note that recursive call to toJSON gets dispatched to the correct implementation.
Now we can use toJSON on lists:
toJSON [1,2,3,4] -----> "[1, 2, 3, 4]"
You can go further and try to implement the whole JSON syntax. Your next step here might be maps. I'll leave it as an exercise.
My point was to explain that when you write instance ToJSON Coord ... you simply provide a way to serialize Coord into JSON. And this gives you an ability to serialize lists of Coords, maps with Coords and many other things. This wouldn't be possible without typeclasses.
In Python, one just does json.loads/json.dumps to handle any kind of object/json-string. Why does the haskell user need to write all these extra code for every object that he seralizes?
An important point is that Python's json.loads wouldn't deserialize json into your object. It would deserialize it into a built in structure that might be equivalent to your object. You can do the same thing in Haskell by using template haskell which would declare ToJSON/FromJSON instances for you. Alternatively you can just dump the JSON into a key value Map and operate on it.
However, writing that extra code (or automatically generating it) gives you a lot of benefits which can be summarized with words "type safety".
For composite objects with multiple hierarchies like ..., do we need to create multiple data and instance at each level?
No you don't. In case of a structure that you linked the instances that would transform a number into such a structure or vice-versa would look approximately like this:
-- | Just a wrapper for the number which must be stored in a nested structure
newtype NestedStructure = NestedStructure Int
instance ToJSON NestedStructure where
toJSON (NestedStructure n) =
object ["a" .= object ["b" .= object ["c" .= n]]]
instance FromJSON NestedStructure where
fromJSON (Object o) = NestedStructure <$> ((o .: "a") >>= (.: "b")
>>= (.: "c"))
fromJSON _ = mzero
In Python, one just does json.loads/json.dumps to handle any kind of
object/json-string. Why does the haskell user need to write all these
extra code for every object that he seralizes?
One way to avoid this is to use the deriving mechanism in combination with GHC.Generics.
The "deriving" mechanism automatically generates typeclass instances for you, avoiding boilerplate. For example:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data VDPServer = VDPServer
{ vdpHost :: String
, vdpPort :: Int
, vdpLogin :: String
, vdpPassword :: String
, vdpDatabase :: String
}
deriving Generic
instance FromJSON VDPServer
instance ToJSON VDPServer
This is descrived in the Type Conversion section of the documentation.
You can customize how the instances are generated using the Options type:
aesonOptions :: Options
aesonOptions = defaultOptions
{ sumEncoding = ObjectWithSingleField
, fieldLabelModifier = tail
, omitNothingFields = True
}
instance FromJSON VDPServer where
parseJSON = genericParseJSON aesonOptions
instance ToJSON VDPServer where
toJSON = genericToJSON aesonOptions
Sometimes, when dealing with complex preexisting JSON schemas, this approach doesn't work so well and one has to fall back to manually defining the parser. But for simpler cases it avoids you a lot of boilerplate.
do we need to create multiple data and instance at each level?
All of the record fields must have their own FromJSON/ToJSON instances. Many common types (tuples, lists, maps, strings...) already have such instances, see the instance list for FromJSON in the documentation. But if not, you'll have to define them (maybe using the Generic trick again).
In Python, one just does json.loads/json.dumps to handle any kind of
object/json-string. Why does the haskell user need to write all these
extra code for every object that he seralizes?
The Haskell equivalent of deserializing a JSON file into a composite of maps, lists, and privitive types is to read a Value object. This way one doesn't have to define a new record.
I'm trying to use Yesod's Persistent module in order to build my database for my website (done in Haskell with Yesod). Here is my models file:
User
idAccount AccountId Int
userLastName Text Maybe
userFirstName Text Maybe
userAge Int Maybe
userSex Text Maybe
userEMail Text
UniqueUserEMail userEMail
Account
accountName Text
accountPassword Text
accountCreatedDate UTCTime default=CURRENT_TIME
accountLastLogin UTCTime default=CURRENT_TIME
UniqueAccountName accountName
When I first compiled, I got the following error:
Model.hs:14:7:
Not in scope: type constructor or class `UTCTime'
In the result of the splice:
$(persistFileWith lowerCaseSettings "config/models")
To see what the splice expanded to, use -ddump-splices
In the second argument of `share', namely
`$(persistFileWith lowerCaseSettings "config/models")'
In the expression:
share
[mkPersist sqlOnlySettings, mkMigrate "migrateAll"]
($(persistFileWith lowerCaseSettings "config/models"))
I then added the time module in my build-depends section in my .cabal file. This removed the last error, but I now have the following errors:
Foundation.hs:135:22:
Not in scope: data constructor `UniqueUser'
Perhaps you meant `UniqueDef' (imported from Yesod)
Foundation.hs:140:23:
`userIdent' is not a (visible) field of constructor `User'
Foundation.hs:141:23:
`userPassword' is not a (visible) field of constructor `User'
For the first error, to my understanding (I.E., what I understand of the uniqueness constraint section of the yesod book), if I want to make a field unique, I just have to add a line at the end of the table definition starting with the string "Unique" with a space and then the name of the field that I want to be unique. Am I mistaken?
As for the last two errors, I do not have those fields declared anywhere, so I do not know why they are there. Any insights on this?
The following compiles for me on persistent-1.3.1.1, persistent-mongoDB-1.4.1, persistent-template 1.3.1.4. I've listed out the pragmas and the modules in case they are a source of the problem.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric #-}
import Database.Persist
import Database.Persist.TH
import Database.Persist.MongoDB
import Language.Haskell.TH.Syntax
import Data.Time.Clock.POSIX (getPOSIXTime,posixSecondsToUTCTime)
import Data.Text (Text)
import Data.Time (UTCTime,TimeOfDay)
let mongoSettings = (mkPersistSettings (ConT ''MongoBackend)) {mpsGeneric = False}
in share [mkPersist mongoSettings] [persistLowerCase|
User
idAccount AccountId Int
userLastName Text Maybe
userFirstName Text Maybe
userAge Int Maybe
userSex Text Maybe
userEMail Text
UniqueUserEMail userEMail
Account
accountName Text
accountPassword Text
accountCreatedDate UTCTime default=CURRENT_TIME
accountLastLogin UTCTime default=CURRENT_TIME
UniqueAccountName accountName
|]
I have found the source of my errors. Since I am using the scaffold site, there is already some stuff implemented for me. One of those things is the Yesod Auth module which was tied with the default Persistent module database structure. So when I changed the database structure in my models file, it was not compatible anymore with the default Auth module code in the Foundation.hs file, namely the getAuthId function:
getAuthId creds = runDB $ do
x <- getBy $ UniqueUser $ credsIdent creds
case x of
Just (Entity uid _) -> return $ Just uid
Nothing -> do
fmap Just $ insert User
( userIdent = credsIdent creds
, userPassword = Nothing
)
I do not know how to modify this code to fit my purposes yet, so I will update this answer once I do.
A short search didn't help me to find the answer, so I started to doubt in its existance. The question is simple. I want to create a polymorphic function, something like this:
f :: String -> String
f s = show (length s)
f :: Int -> String
f i = show i
A function defined differently for different data types is meant. Is it possible? If yes, how?
There are two flavors of polymorphism in Haskell:
parameteric polymorphism; and
bounded polymorphism
The first is the most general -- a function is parametrically polymorphic if it behaves uniformly for all types, in at least one of its type parameters.
For example, the function length is polymorphic -- it returns the length of a list, no matter what type is stored in its list.
length :: [a] -> Int
The polymorphism is indicated by a lower case type variable.
Now, if you have custom behavior that you want to have for a certain set of types, then you have bounded polymorphism (also known as "ad hoc"). In Haskell we use type classes for this.
The class declares which function will be available across a set of types:
class FunnyShow a where
funnyshow :: a -> String
and then you can define instances for each type you care about:
instance FunnyShow Int where
funnyshow i = show (i+1)
and maybe:
instance FunnyShow [Char] where
funnyshow s = show (show s)
Here is how you can achieve something similar using type families.
Well if you have same return types then you can achieve the behaviour without using type families and just using type classes alone as suggested by Don.
But it is better to use type families when you want to support more complex adhoc polymorphism, like different return types for each instance.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE TypeFamilies #-}
class F a where
type Out a :: *
f :: a -> Out a
instance F String where
type Out String = String
f = show . length
instance F Int where
type Out Int = String
f = show
instance F Float where
type Out Float = Float
f = id
In ghci
*Main> f (2::Int)
"2"
*Main> f "Hello"
"5"
*Main> f (2::Float)
2.0
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!
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}}