Today I wanted to solve next problem.
Assume that we have typeclass DataWithDefault
defined as
class DataWithDefault a where
defaultValue :: a
And we have data Example
defined as
data Example =
Example { field1 :: Text
, field2 :: Text
} deriving (Show)
instance DataWithDefault Example where
defaultValue = Example "Hello" "World"
instance FromJSON Example where
parseJSON (Object v) =
Example <$> v .:? "field1" .!= field1 defaultValue
<*> v .:? "field2" .!= field2 defaultValue
parseJSON _ = mzero
instance ToJSON Example where
toJSON (Example f1 f2) =
object [ "field1" .= f1
, "field2" .= f2
]
I know that Aeson uses Generics to derive FromJSON
and ToJSON
instances automatically, but I can't figure out how to make it derive FromJSON
instance with default values for fields that are not represented in given json. Is it possible to do using generics? Actually I don't ask you a final solution, but maybe some clue?
Update
Let me add more information about the problem.
Suppose now that you need to update your Example
data and now it defined as
data Example =
Example { field1 :: Text
, field2 :: Text
, field3 :: Int
} deriving (Show)
So you want to update DataWithDefault
instance declaration
instance DataWithDefault Example where
defaultValue = Example "Hello" "World" 12
And what I want to do is not to write
instance FromJSON Example where
parseJSON (Object v) =
Example <$> v .:? "field1" .!= field1 defaultValue
<*> v .:? "field2" .!= field2 defaultValue
<*> v .:? "field3" .!= field3 defaultValue
parseJSON _ = mzero
And want to derive such instance definition automatically. And more importantly, I want to do it not only for Example
, but for DataWithDefault a
.
Update 2
The point of combining .:?
and .!=
is to get as much as possible fields from given json and set every missing field to it's default value. So when we pass
{ "field1" : "space", "field2" : "ship" }
I want my new example be not field1 = Hello; field2 = World; field3 = 12
, but field1 = space; field2 = ship; field3 = 12
.
Instead of making Aeson do it, just use a newtype for what they were designed for:
newtype DefaultJSON a = DefaultJSON { unDefaultJSON :: a }
instance (FromJSON a, DataWithDefault a) => FromJSON (DefaultJSON a) where
parseJSON v = DefaultJSON <$> (parseJSON v <|> pure defaultValue)
Then you can do
> decode "{}" :: Maybe (DefaultJSON Example)
Just (DefaultJSON {unDefaultJSON = (Example {field1 = "Hello", field2 = "World"}})
This is a little different than what you asked, it provides a default value in case parsing fails, but not a default value for each field in case that individual field is missing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With