首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Aeson进行错误校验

用Aeson进行错误校验
EN

Stack Overflow用户
提问于 2014-09-11 01:24:51
回答 1查看 537关注 0票数 2

此代码将递归的JSON结构解析为我创建的haskell对象。我在使用伊索图书馆。我遇到的问题是,即使使用递归调用,我也希望能够轻松地进行错误检查。现在,每当发生错误时,我都使用一个虚拟值(ayyLmao)。然而,我想利用从Parser monad获得的错误检查。我如何做到这一点,并可能清理我的代码在过程中?如果有必要,我还可以发布一些示例JSON。

编辑:我想指出,我想去掉"ayyLmao“(这是个愚蠢的名字),用'mzero‘来代替我的错误检查。

代码语言:javascript
复制
type Comments = Vector Comment

data Comment = Comment
    { author :: Text
    , body :: Text
    , replies :: Comments
    } deriving Show

-- empty placeholder value (only should appear when errors occur)
ayyLmao :: Comment
ayyLmao = Comment "Ayy" "Lmao" V.empty

parseComment :: Object -> Maybe Comments
parseComment obj = flip parseMaybe obj $ \listing -> do
    -- go through intermediate objects
    comments <- listing .: "data" >>= (.: "children")
    -- parse every comment in an array
    return $ flip fmap comments $ \commentData -> case commentData of
        -- if the data in the array is an object, parse the comment
        -- (using a dummy value on error)
        Object v -> fromMaybe ayyLmao (parseMaybe parseComment' v)
        -- use a dummy value for errors (we should only get objects in
        -- the array
        _ -> ayyLmao
        where
            parseComment' :: Object -> Parser Comment
            parseComment' v = do
                -- get all data from the object
                comment <- v .: "data"
                authorField <- comment .: "author"
                bodyField <- comment .: "body"
                replyObjs <- comment .: "replies"
                return $ case replyObjs of
                    -- if there are more objects, then parse recursively
                    Object more -> case parseComment more of
                        -- errors use the dummy value again
                        Just childReplies -> Comment authorField bodyField childReplies
                        Nothing -> ayyLmao
                    -- otherwise, we've reached the last comment in the
                    -- tree
                    _ -> Comment authorField bodyField V.empty

编辑:下面答案中的代码是正确的,但我想添加修改后的解决方案。给出的解决方案假定"null“没有指示更多的答复,但出于某种原因,API设计人员决定应该用空字符串来表示它。

代码语言:javascript
复制
instance FromJSON Comment where
    parseJSON = withObject "Comment" $ \obj -> do
        dat <- obj .: "data"
        commReplies <- dat .: "replies"
        Comment
            <$> dat .: "author"
            <*> dat .: "body"
            <*> case commReplies of
                Object _  -> getComments <$> dat .: "replies"
                String "" -> return V.empty
                _         -> fail "Expected more comments or a the empty string"
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-09-11 04:44:01

您可以使用“或者我可以有一个解析器列表,然后将其折叠成一个更大的解析器”。这正是从嵌套解析器传播错误的方式。删除ayyLmao的代码的最小更改是:

代码语言:javascript
复制
parseComment :: Object -> Maybe Comments
parseComment obj = flip parseMaybe obj $ \listing -> do
    -- go through intermediate objects
    comments <- listing .: "data" >>= (.: "children")
    -- parse every comment in an array
    V.sequence $ flip fmap comments $ \commentData -> case commentData of
        -- if the data in the array is an object, parse the comment
        -- (using a dummy value on error)
        Object v -> parseComment' v
        -- use a dummy value for errors (we should only get objects in
        -- the array
        _ -> mzero
        where
            parseComment' :: Object -> Parser Comment
            parseComment' v = do
                -- get all data from the object
                comment <- v .: "data"
                authorField <- comment .: "author"
                bodyField <- comment .: "body"
                replyObjs <- comment .: "replies"
                case replyObjs of
                    -- if there are more objects, then parse recursively
                    Object more -> case parseComment more of
                        -- errors use the dummy value again
                        Just childReplies -> return $ Comment authorField bodyField childReplies
                        Nothing -> mzero
                    -- otherwise, we've reached the last comment in the
                    -- tree
                    _ -> return $ Comment authorField bodyField V.empty

这将对错误用例使用mzero,并从带有V.sequence的答复列表中传播错误。sequence恰恰是一种东西,它接收解析器列表(在本例中是向量)并折叠成一个解析器,这些解析器要么成功要么失败。

然而,以上这些并不是一个很好的方式使用伊索。通常更好的做法是从那里派生FromJSON类型类的实例并从中工作。我会将上面的内容作为

代码语言:javascript
复制
{-# LANGUAGE OverloadedStrings #-}

import qualified Data.Vector as V
import Data.Vector (Vector)
import Data.Text (Text)
import Data.Aeson
import Data.Maybe (fromMaybe)

import Control.Applicative

type Comments = Vector Comment

data Comment = Comment
    { author :: Text
    , body :: Text
    , replies :: Comments
    } deriving Show

newtype CommentList = CommentList { getComments :: Comments }

instance FromJSON Comment where
    parseJSON = withObject "Comment" $ \obj -> do
        dat <- obj .: "data"
        Comment
            <$> dat .: "author"
            <*> dat .: "body"
            <*> (fromMaybe V.empty . fmap getComments <$> dat .: "replies")

instance FromJSON CommentList where
    parseJSON = withObject "CommentList" $ \obj -> do
        dat <- obj .: "data"
        CommentList <$> dat .: "children"

这引入了一个包装器类型CommentList,它用于从JSON获取obj.data.children属性。这利用了Vector现有的Vector实例的优势,因此您不必手动遍历答复并分别解析它们。

表达式

代码语言:javascript
复制
fromMaybe V.empty . fmap getComments <$> dat .: "replies"

假设JSON中的replies属性包含一个null值或一个有效的CommentList,因此它尝试解析一个Maybe CommentList值(null被解析为Nothing),然后使用fromMaybe用空向量替换Nothing值。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25777569

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档