【问题标题】:Parsing an Options Object to a List of Options将选项对象解析为选项列表
【发布时间】:2015-04-13 15:33:02
【问题描述】:

我一直在针对基于 JSON 的 API 开发一个小型库。该库使用“选项”对象,即一系列指定高级行为的键值对:

{
  "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)]

我遇到的问题在于OptionsfromJSON 实现。我之前看到的所有用例都为数据表示映射提供了一个相当简单的 json-object-representation。我需要做的是将对象转换为选项对象到数据(选项)表示的列表。例如,我在开始时给出的“选项”下的示例 JSON 必须变为:

Options [NotifyNoData 20, Silenced (Just (posixSecondsToUTCTime 1428937807))]

FromJSON 需要实现parseJSON :: FromJSON a => Value -> Parser a。我无法理解如何使用 API 提供的这个可选对象结构来构建解析器。是否有将 JSON 对象解析为这样的列表的标准方法?可能是我没有完全理解 Parser 类型类。

【问题讨论】:

    标签: json parsing haskell aeson


    【解决方案1】:

    您也许可以使用解析器单子来提取所有信息,例如:

    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,我想您对此有所了解)。

    对其他类型的选项执行相同的操作,连接结果。

    【讨论】:

      【解决方案2】:

      使用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 实例(或者您可以只使用“整数”和某种 fromposixtime 等)。

      【讨论】:

        猜你喜欢
        • 2020-06-15
        • 2011-02-03
        • 2015-05-25
        • 1970-01-01
        • 2019-11-26
        • 1970-01-01
        • 1970-01-01
        • 2013-10-03
        • 2011-12-16
        相关资源
        最近更新 更多