Haskell Yesod - 在 json 中提取对象,然后再将其转换为模型
Haskell Yesod - extracting a object inside a json before converting it to a model
假设我有一个这样的JSON:
{
data: {...}
}
和{...}
代表我的一个模型。怎么可能
在这种情况下,在处理程序中获取我的模型?例如,以下显然不起作用:
putMyEntityR :: Handler ()
putMyEntityR = do
(Entity id _) <- (...) -- getting the Key
e <- requireJsonBody :: Handler MyEntity
runDB $ replace id e
sendResponseStatus status204 ("UPDATED" :: Text)
如何读取 JSON
,获取 data
对象,然后才对其进行解码?
这太大而不适合我上面的评论(使用 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 实例
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。
假设我有一个这样的JSON:
{
data: {...}
}
和{...}
代表我的一个模型。怎么可能
在这种情况下,在处理程序中获取我的模型?例如,以下显然不起作用:
putMyEntityR :: Handler ()
putMyEntityR = do
(Entity id _) <- (...) -- getting the Key
e <- requireJsonBody :: Handler MyEntity
runDB $ replace id e
sendResponseStatus status204 ("UPDATED" :: Text)
如何读取 JSON
,获取 data
对象,然后才对其进行解码?
这太大而不适合我上面的评论(使用 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 实例
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。