Haskell Yesod - 在将json转换为模型之前提取json中的对象

时间:2016-03-05 15:41:58

标签: haskell yesod aeson

假设我有这样的JSON:

{
  data: {...}
}

{...}代表了我的模型。怎么可能 在Handler中获取我的模型?例如,以下内容显然不起作用:

putMyEntityR :: Handler ()
putMyEntityR = do
  (Entity id _) <- (...) -- getting the Key
  e <- requireJsonBody :: Handler MyEntity
  runDB $ replace id e
  sendResponseStatus status204 ("UPDATED" :: Text)

如何阅读JSON,获取data对象,然后才对其进行解码?

2 个答案:

答案 0 :(得分:2)

这太大了,不适合我上面的评论(使用lens-aeson

λ: import Control.Lens
λ: import Data.Aeson.Lens
λ: let e = "{ \"data\": [1,2,3] }"
λ: e ^? key "data"
Just (Array [Number 1.0,Number 2.0,Number 3.0])

正如Carsten所说,你仍然需要提供模型的FromJSON实例

答案 1 :(得分:1)

Github Issue上对这个问题进行了更多的讨论,我在这里作为答案添加,因为它更加充实。这是我们使用下面定义的辅助函数得到的Handler函数:

postDataCommentR :: Handler Value
postDataCommentR = do
  value <- requireJsonBody' -- Parse request body into Value
  commentJson <- requireJsonKey "data" value -- Lookup a key from the Value
  comment <- (requireJsonParse commentJson :: Handler Comment) -- Parse the Value into a comment record

  insertedComment <- runDB $ insertEntity comment
  returnJson insertedComment

这些函数接受请求体并将其解析为aeson Value

import qualified Data.Aeson as J
import qualified Data.Aeson.Parser as JP
import Data.Conduit.Attoparsec (sinkParser)

-- These two functions were written by @FtheBuilder
parseJsonBody' :: (MonadHandler m) => m (J.Result Value)
parseJsonBody' = do
    eValue <- rawRequestBody $$ runCatchC (sinkParser JP.value')
    return $ case eValue of
        Left e -> J.Error $ show e
        Right value -> J.Success value

-- | Same as 'parseJsonBody', but return an invalid args response on a parse
-- error.
requireJsonBody' :: (MonadHandler m) => m Value
requireJsonBody' = do
    ra <- parseJsonBody'
    case ra of
        J.Error s -> invalidArgs [pack s]
        J.Success a -> return a

这些辅助函数用于将Value解析为记录:

requireJsonParse :: (MonadHandler m, FromJSON a) => Value -> m a
requireJsonParse v = case J.fromJSON v of
  J.Error s -> invalidArgs [pack s]
  J.Success a -> return a

requireJsonKey :: (MonadHandler m) => Text -> Value -> m Value
requireJsonKey key jObject@(Object hashMap) = case lookup key hashMap of
                                    Nothing -> invalidArgs ["Couldn't find a value when looking up the key " <> key <> " in the object: " <> (pack (show jObject))]
                                    Just v -> return v
requireJsonKey key invalid = invalidArgs ["When looking up the key " <> key <> ", expected an object but got a " ++ (pack (show invalid))]

评论

埃宋透镜

我没有使用aeson-lens,但代码非常相似,有或没有它,因为我们只是深入一个键。如果我们深入了解JSON,aeson-lens会让事情变得更好。

与包装器定义的比较

一旦定义了辅助函数,你仍然需要几行来解析Value,然后查找data密钥,然后创建你的记录。你可以做一些事情来缩短它,但最终@Carsten推荐的封装器长度相似,复杂度更低,imo。