简体   繁体   English

如何用aeson解析嵌套的JSON

[英]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: 不幸的是,我面临两个我没有找到解决方案的问题,即:

  1. 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? 我认为这是一个问题我错了吗?

  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM