如何使用Data.Aeson在JSON解析中正确地输出错误

时间:2015-02-13 07:04:04

标签: parsing haskell monads applicative aeson

我的类型和相应的FromJSON实现,如下所示。

nonEmptyList变为Maybe NonEmpty,我正在尝试正确处理List确实为空的情况,我必须中止解析。这个解析实际上是在parseJsonBody内部完成的,这意味着我不希望error "foo"以我的方式离开它,但我想返回mzero(或其他任何可以做的伎俩,mzero是我到目前为止唯一偶然发现的事情),以便处理程序正确返回400而不是使用500崩溃。

下面的方法编译,但据我所知,它几乎等于error或在parseJSON内部抛出的其他形式的异常。但是,如果我返回mzero(例如,使用<*> mzero而不是该行),则会失败。

import qualified Data.List.NonEmpty as NE
data GSAnswer = GSAnswer { gsAnswerQuestionId :: Int
                         , gsAnswerResponses :: NE.NonEmpty GSResponse
                         } deriving (Show, Eq)


instance FromJSON GSAnswer where
  parseJSON (Object o) =
    GSAnswer <$> o .: "question-id"
             -- how do I return mzero here based on NE.nonEmpty?
             -- this will throw an exception right now on an empty list
             <*> fmap (fromMaybe (fail "foo") . NE.nonEmpty) (o .: "responses")
  parseJSON _ = mzero

一种选择是以某种方式对fmap NE.nonEmpty (o .: "responses")的结果进行模式匹配,但我无法弄清楚模式会是什么:看起来Parser看起来有没有任何构造函数?

1 个答案:

答案 0 :(得分:3)

基本上,你需要一个Parser [a] -> Parser NE.NonEmpty变换器,这相对容易:

-- toNonEmptyP :: Parser [a] -> Parser NE.NonEmtpy
toNonEmptyP p = fmap NE.nonEmpty p >>= maybe mzero return

我们在常规列表解析器上映射NE.nonEmpty,这会给我们Parser (Maybe NE.NonEmpty)。然后,我们使用Maybe检查maybe,如果是mzero则使用Nothing,或者将解析后的值return返回到解析上下文。您的FromJSON实例然后归结为

instance FromJSON GSAnswer where
  parseJSON (Object o) =
    GSAnswer <$> o .: "question-id"
             <*> toNonEmptyP (o .: "responses")
  parseJSON _ = mzero

您可以使用fail msg代替mzero来提供自定义错误消息,因为fail :: String -> Parser a没有触底。