[英]How to parse nested JSON with aeson
I am trying to parse JSON of the following form using aeson 我试图使用aeson解析以下形式的JSON
{"field":{"name":"..."}}
or
{"tag":{"name":"..."}}
or
{"line":{"number":"..."}}
to construct the following data type 构造以下数据类型
data Rule = Line Integer
| Field L.ByteString
| Tag L.ByteString
Unfortunately, I face two problems that I've not found solutions to, namely: 不幸的是,我面临两个我没有找到解决方案的问题,即:
How do I parse nested JSON? 如何解析嵌套的JSON? Looking at the implementation of (.:) , it uses lookup to extract a specific key's value.
查看(。:)的实现,它使用lookup来提取特定键的值。 I'm hesitant to do something like this as it seems to be relying too much on the specifics of how aeson implements things.
我对做这样的事情犹豫不决,因为它似乎过分依赖于aeson如何实现事物的具体细节。 Am I wrong in thinking this is an issue?
我认为这是一个问题我错了吗?
How do I use the correct data constructor based on which key is present in the JSON? 如何根据JSON中存在的键使用正确的数据构造函数? All my efforts with <|> have led me nowhere.
我用<|>做的所有努力都让我无处可去。
I would post the code I've written thus far, but I haven't even gotten to the point where I have anything worth posting. 我会发布到目前为止我写的代码,但我还没有达到我有什么值得发布的地步。
How about the following? 以下怎么样?
{-# 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"
For this problem I created a helper function that looks up a key: 对于这个问题,我创建了一个查找键的辅助函数:
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"
and using it two functions that nest into objects: 并使用它嵌入对象的两个函数:
(.:*) :: (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
Only lookupE
depends on the internal representation so it's easy to modify it, if that changes. 只有
lookupE
依赖于内部表示,因此如果更改,则很容易对其进行修改。 Then {"tag":{"name":"..."}}
is parsed as v .:* ["tag", "name"]
. 然后
{"tag":{"name":"..."}}
被解析为v .:* ["tag", "name"]
。 Note that it also works for empty lists - v .:* []
is equivalent to parseJSON v
. 请注意,它也适用于空列表 -
parseJSON v
v .:* []
等同于parseJSON v
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.