将选项对象解析为选项列表
Parsing an Options Object to a List of Options
我一直在针对基于 JSON 的 API 开发一个小型库。这个库使用了一个 "options" 对象,一系列指定高级行为的键值对:
{
"id": 1234
...
"options": {
"notify_no_data": True,
"no_data_timeframe": 20,
"notify_audit": False,
"silenced": {"*" :1428937807}
}
}
我在 Haskell 中使用选项列表表示选项概念:
data Option = NotifyNoData NominalDiffTime -- notify after xyz
| NotifyAudit -- notify on changes
| Silenced (Maybe UTCTime) -- silence all notifications ("*") until xyz (or indefinitely)
newtype Options = Options [Option]
实施 toJSON
非常简单;我为 Option
类型实例化了 toJSON
,然后将它们用作 Options
类型的助手。
instance ToJSON Option where
toJSON (Silenced mtime) =
Object $ Data.HashMap.fromList [("silenced", mapping)]
where stamp = maybe Null (jsonTime . floor . utcTimeToPOSIXSeconds) mtime
mapping = Object $ Data.HashMap.singleton "*" stamp
toJSON (NotifyNoData difftime) =
Object $ Data.HashMap.fromList [("notify_no_data", Bool True)
,("no_data_timeframe", stamp)]
where stamp = jsonTime $ floor (difftime / 60)
toJSON NotifyAudit =
Object $ Data.HashMap.fromList [("notify_audit", Bool True)]
instance ToJSON Options where
toJSON (Options options) = Object $ Data.HashMap.unions $ reverse $ (opts:) $ map ((\(Object o) -> o) . toJSON) options
where opts = Data.HashMap.fromList [("silenced", Object Data.HashMap.empty)
,("notify_no_data", Bool False)
,("notify_audit", Bool False)]
我 运行 遇到的问题是 Options
的 fromJSON
实现。我之前看到的所有用例都提供了一个相当简单的 json-object-representation 到 data-representation 映射。我需要做的是将对象转换为选项对象,再转换为数据 (Option) 表示列表。比如我一开始给的"options"下的样本JSON就得变成:
Options [NotifyNoData 20, Silenced (Just (posixSecondsToUTCTime 1428937807))]
FromJSON
需要 parseJSON :: FromJSON a => Value -> Parser a
的实现。我无法理解如何使用 API 给出的这个可选对象结构来构建解析器。是否有一种标准方法可以将 JSON 对象解析为这样的列表?可能是我没有完全理解 Parser
类型类。
您也许可以使用解析器 monad 来提取所有信息,例如:
parseJSON (Object v) = do
maybeNotify <- v .:? "notify_no_data"
maybeTimeFrame <- v .:? "no_data_timeframe"
let nots = case (maybeNotify,maybeTimeFrame) of
(Just True,Just stamp) -> [NotifyNoData $ fromJsontime stamp]
_ -> []
return $ Options nots
(fromJsontime 只是一个辅助函数,用于将 JSON 值转换为您的 NominalDiffTime,我想您对此有所了解)。
对其他类型的选项执行相同的操作,连接结果。
使用 json-stream 解析器(我是其作者),它看起来像这样(根据我的想法,未经测试):
option = NotifyNoData <$> "no_data_timeframe" .: value
<* filterI id ("notify_no_data" .: bool)
<|> const NotifyAudit <$> filterI id ("notify_audit" .: bool)
<|> Silenced <$> "silenced" .: "*" .:? value
optionList = Options <$> toList option
代码要求您拥有 UTCTime 的 FromJSON 实例(或者您可以只使用 'integer' 和某种 fromposixtime 等)。
我一直在针对基于 JSON 的 API 开发一个小型库。这个库使用了一个 "options" 对象,一系列指定高级行为的键值对:
{
"id": 1234
...
"options": {
"notify_no_data": True,
"no_data_timeframe": 20,
"notify_audit": False,
"silenced": {"*" :1428937807}
}
}
我在 Haskell 中使用选项列表表示选项概念:
data Option = NotifyNoData NominalDiffTime -- notify after xyz
| NotifyAudit -- notify on changes
| Silenced (Maybe UTCTime) -- silence all notifications ("*") until xyz (or indefinitely)
newtype Options = Options [Option]
实施 toJSON
非常简单;我为 Option
类型实例化了 toJSON
,然后将它们用作 Options
类型的助手。
instance ToJSON Option where
toJSON (Silenced mtime) =
Object $ Data.HashMap.fromList [("silenced", mapping)]
where stamp = maybe Null (jsonTime . floor . utcTimeToPOSIXSeconds) mtime
mapping = Object $ Data.HashMap.singleton "*" stamp
toJSON (NotifyNoData difftime) =
Object $ Data.HashMap.fromList [("notify_no_data", Bool True)
,("no_data_timeframe", stamp)]
where stamp = jsonTime $ floor (difftime / 60)
toJSON NotifyAudit =
Object $ Data.HashMap.fromList [("notify_audit", Bool True)]
instance ToJSON Options where
toJSON (Options options) = Object $ Data.HashMap.unions $ reverse $ (opts:) $ map ((\(Object o) -> o) . toJSON) options
where opts = Data.HashMap.fromList [("silenced", Object Data.HashMap.empty)
,("notify_no_data", Bool False)
,("notify_audit", Bool False)]
我 运行 遇到的问题是 Options
的 fromJSON
实现。我之前看到的所有用例都提供了一个相当简单的 json-object-representation 到 data-representation 映射。我需要做的是将对象转换为选项对象,再转换为数据 (Option) 表示列表。比如我一开始给的"options"下的样本JSON就得变成:
Options [NotifyNoData 20, Silenced (Just (posixSecondsToUTCTime 1428937807))]
FromJSON
需要 parseJSON :: FromJSON a => Value -> Parser a
的实现。我无法理解如何使用 API 给出的这个可选对象结构来构建解析器。是否有一种标准方法可以将 JSON 对象解析为这样的列表?可能是我没有完全理解 Parser
类型类。
您也许可以使用解析器 monad 来提取所有信息,例如:
parseJSON (Object v) = do
maybeNotify <- v .:? "notify_no_data"
maybeTimeFrame <- v .:? "no_data_timeframe"
let nots = case (maybeNotify,maybeTimeFrame) of
(Just True,Just stamp) -> [NotifyNoData $ fromJsontime stamp]
_ -> []
return $ Options nots
(fromJsontime 只是一个辅助函数,用于将 JSON 值转换为您的 NominalDiffTime,我想您对此有所了解)。
对其他类型的选项执行相同的操作,连接结果。
使用 json-stream 解析器(我是其作者),它看起来像这样(根据我的想法,未经测试):
option = NotifyNoData <$> "no_data_timeframe" .: value
<* filterI id ("notify_no_data" .: bool)
<|> const NotifyAudit <$> filterI id ("notify_audit" .: bool)
<|> Silenced <$> "silenced" .: "*" .:? value
optionList = Options <$> toList option
代码要求您拥有 UTCTime 的 FromJSON 实例(或者您可以只使用 'integer' 和某种 fromposixtime 等)。