我有一个我想从JSON解析的记录:
data ArticleInfo = ArticleInfo {
author :: String,
title :: String,
pubDate :: Day
} deriving (Show, Eq)
instance FromJSON ArticleInfo where
parseJSON (Object value) = ArticleInfo <$>
value .: "author" <*>
value .: "title" <*>
liftM parsePubDate (value .: "pubDate")
parseJSON _ = mzero
parsePubDate :: String -> Day
parsePubDate = parseTimeOrError True defaultTimeLocale "%Y-%-m-%-d"
parseArticleInfo :: String -> Maybe ArticleInfo
parseArticleInfo source = Data.Yaml.decode (pack source)
这样可行,但在不符合"%Y-%-m-%-d"
格式说明符的格式不正确的日期崩溃。解析器规范目前在我头顶 - 当我遇到这样的日期时,我在那里写什么来使ArticleInfo
解析器返回Nothing
?
(我知道我应该首先将parseTimeOrError
替换为parseTime
,但这就是它。甚至parseTime
也被弃用,告诉我使用parseTimeM
签名我还不明白。)
答案 0 :(得分:2)
这是Maybe
monad的典型用法。对于初学者来说,这并不是那么明显,但是一旦你了解monad就会成为第二天性。
parsePubDate :: String -> Maybe Day
parsePubDate = parseTimeM True defaultTimeLocale "%Y-%-m-%-d"
答案 1 :(得分:2)
如果你没有使整个解析器失败,如果有一个无效的日期,你可以在没有太多麻烦的情况下解决它:
parseJSON (Object value) = ArticleInfo <$>
value .: "author" <*>
value .: "title" <*>
(value .: "pubDate" >>= parseTimeM True defaultTimeLocale "%Y-%-m-%-d")
相反,如果您需要Maybe ArticleInfo
的解析器(我最初想到的,但现在意识到您可能不想要),请继续阅读...
首先,如果您要解析Maybe ArticleInfo
,您的FromJSON
实例将需要Maybe ArticleInfo
,而不是ArticleInfo
。 (这样做需要语言扩展名FlexibleInstances
。)其次,您还需要更改parsePubDate
以返回Maybe Day
,并将parseTimeOrError
替换为{ {1}}。 (事实上,当parseTimeM
专注于parseTimeM
时,m ~ Monad
的功能与弃用的parseTimeM
相同。)
然后事情变得复杂一些。有点冗长,你可以这样做:
parseTime
...但这有点重复,并不是一个非常“好”的解决方案。您也可以使用MaybeT
monad变换器,这可能会更好一些:
parseJSON (Object value) = (\a t pd -> ArticleInfo a t <$> pd) <$>
value .: "author" <*>
value .: "title" <*>
liftM parsePubDate (value .: "pubDate")
此外,所有这些都使parseJSON (Object value) =
runMaybeT $ ArticleInfo <$> lift (value .: "author")
<*> lift (value .: "title")
<*> (MaybeT $ parsePubDate <$> value .: "pubDate")
返回Data.Yaml.decode (pack source)
。您可以使用Maybe (Maybe ArticleInfo)
将join
折叠起来。