将选项对象解析为选项列表

时间:2015-04-13 15:33:03

标签: json parsing haskell aeson

我一直在针对基于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类型类。

2 个答案:

答案 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等)。