【问题标题】:Aeson : generics with default valuesAeson:具有默认值的泛型
【发布时间】:2014-10-31 21:31:16
【问题描述】:

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

假设我们将 typeclass 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 使用泛型自动派生 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

【问题讨论】:

  • 你不能fromMaybe defaultValue $ decode jsonContents吗?
  • 如果您正在创建自己的类型类,您可以考虑使用 Data.Default 来获取大量结构的实例

标签: haskell aeson


【解决方案1】:

与其让 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"}})

这和你问的有点不同,它提供了一个默认值以防解析失败,但不提供每个字段的默认值,以防个别字段丢失。

【讨论】:

  • 嗯,是的,我可以这样做,但如果它能为每个缺失的字段设置默认值,那就太好了。
  • 哦,我没想到Aeson会这样,我只是想弄清楚我的问题是否存在解决方案(合理的):D
  • @d12frosted 前几天我写了this 回答,这对于单个字段来说是一个很好的解决方案,但显然我不会认为它对记录中的所有字段都是合理的。也许它可以给你一些想法?
猜你喜欢
  • 1970-01-01
  • 2018-09-12
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 2018-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多