今天我想解决下一个问题。
假设我们将类型DataWithDefault
定义为
class DataWithDefault a where
defaultValue :: a
我们将数据Example
定义为
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
]
我知道Aeson使用Generics自动派生FromJSON
和ToJSON
个实例,但我无法弄清楚如何使用默认值派生FromJSON
实例没有在给定的json中表示。是否可以使用泛型?其实我不会问你最终的解决方案,但也许有些线索?
更新
让我添加有关此问题的更多信息。
现在假设您需要更新Example
数据,现在定义为
data Example =
Example { field1 :: Text
, field2 :: Text
, field3 :: Int
} deriving (Show)
所以你想要更新DataWithDefault
实例声明
instance DataWithDefault Example where
defaultValue = Example "Hello" "World" 12
我想做的不是写
instance FromJSON Example where
parseJSON (Object v) =
Example <$> v .:? "field1" .!= field1 defaultValue
<*> v .:? "field2" .!= field2 defaultValue
<*> v .:? "field3" .!= field3 defaultValue
parseJSON _ = mzero
并希望自动导出这样的实例定义。更重要的是,我希望不仅针对Example
,而且针对DataWithDefault a
。
更新2
组合.:?
和.!=
的关键是从给定的json中获取尽可能多的字段,并将每个缺少的字段设置为默认值。所以当我们通过时
{ "field1" : "space", "field2" : "ship" }
我希望我的新示例不是field1 = Hello; field2 = World; field3 = 12
,而是field1 = space; field2 = ship; field3 = 12
。
答案 0 :(得分:3)
不要让Aeson这样做,只需使用新类型来实现它们的目的:
newtype DefaultJSON a = DefaultJSON { unDefaultJSON :: a }
instance (FromJSON a, DataWithDefault a) => FromJSON (DefaultJSON a) where
parseJSON v = DefaultJSON <$> (parseJSON v <|> pure defaultValue)
然后你可以做
> decode "{}" :: Maybe (DefaultJSON Example)
Just (DefaultJSON {unDefaultJSON = (Example {field1 = "Hello", field2 = "World"}})
这与您提出的内容略有不同,它在解析失败时提供默认值,但在缺少单个字段的情况下不提供每个字段的默认值。