我一直在针对基于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
很简单;我为toJSON
类型实例化Option
,然后将其用作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)]
我遇到的问题出现在fromJSON
的{{1}}实施中。我之前看到的所有用例都为数据表示映射提供了一个相当简单的json-object表示。我需要做的是将对象转换为选项对象到数据列表(选项)表示。例如,我在开始时给出的“选项”下的示例JSON必须变为:
Options
Options [NotifyNoData 20, Silenced (Just (posixSecondsToUTCTime 1428937807))]
需要FromJSON
的实施。我无法理解如何使用API提供的可选对象结构来构建Parser。是否有一种标准方法将JSON对象解析为这样的列表?可能是因为我无法完全理解parseJSON :: FromJSON a => Value -> Parser a
类型类。
答案 0 :(得分:4)
您可以使用解析器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,我想你有一些东西可以用来)。
对其他类型的选项执行相同操作,连接结果。
答案 1 :(得分:1)
使用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实例(或者你可以使用&#39;整数&#39;以及某种fromposixtime等)。