我正在尝试使用aeson解析以下表单的JSON
{"field":{"name":"..."}}
or
{"tag":{"name":"..."}}
or
{"line":{"number":"..."}}
构造以下数据类型
data Rule = Line Integer
| Field L.ByteString
| Tag L.ByteString
不幸的是,我遇到了两个我没有找到解决方案的问题,即:
如何解析嵌套的JSON?查看(.:)的实现,它使用lookup来提取特定键的值。我对做这样的事情犹豫不决,因为它似乎过分依赖于aeson如何实现事物的具体细节。我认为这是一个问题我错了吗?
如何根据JSON中存在的键使用正确的数据构造函数?我所有的努力与< |>让我无处可去。
我会发布到目前为止我写的代码,但我还没有达到我有什么值得发布的地步。
答案 0 :(得分:9)
以下情况如何?
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Map as M
data Rule = Line Integer
| Field L.ByteString
| Tag L.ByteString
deriving Show
instance FromJSON Rule where
parseJSON j = do
o <- parseJSON j -- takes care of JSON type check
case M.toList (o :: Object) of
[("field", Object o')] -> Field <$> o' .: "name"
[("tag", Object o')] -> Tag <$> o' .: "name"
[("line", Object o')] -> Line <$> o' .: "number"
_ -> fail "Rule: unexpected format"
答案 1 :(得分:6)
对于这个问题,我创建了一个查找键的辅助函数:
lookupE :: Value -> Text -> Either String Value
lookupE (Object obj) key = case H.lookup key obj of
Nothing -> Left $ "key " ++ show key ++ " not present"
Just v -> Right v
loopkupE _ _ = Left $ "not an object"
并使用它嵌入对象的两个函数:
(.:*) :: (FromJSON a) => Value -> [Text] -> Parser a
(.:*) value = parseJSON <=< foldM ((either fail return .) . lookupE) value
(.:?*) :: (FromJSON a) => Value -> [Text] -> Parser (Maybe a)
(.:?*) value = either (\_ -> return Nothing) (liftM Just . parseJSON)
. foldM lookupE value
-- Or more simply using Control.Alternative.optional
-- (.:?*) value keys = optional $ value .:* keys
只有lookupE
取决于内部表示,因此如果更改,则很容易修改它。然后{"tag":{"name":"..."}}
被解析为v .:* ["tag", "name"]
。请注意,它也适用于空列表 - v .:* []
相当于parseJSON v
。