5

今天我想解决下一个问题。

假设我们将 typeclassDataWithDefault定义为

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 使用泛型自动派生FromJSONToJSON实例,但我不知道如何让它派生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

4

1 回答 1

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"}})

这与您所要求的略有不同,它提供了一个默认值以防解析失败,但不提供每个字段的默认值以防缺少单个字段。

于 2014-10-31T22:05:59.337 回答