如何用aeson解析嵌套的JSON

时间:2011-07-26 07:28:51

标签: json haskell

我正在尝试使用aeson解析以下表单的JSON

{"field":{"name":"..."}}

or

{"tag":{"name":"..."}}

or

{"line":{"number":"..."}}

构造以下数据类型

data Rule = Line Integer
          | Field L.ByteString
          | Tag L.ByteString

不幸的是,我遇到了两个我没有找到解决方案的问题,即:

  1. 如何解析嵌套的JSON?查看(.:)的实现,它使用lookup来提取特定键的值。我对做这样的事情犹豫不决,因为它似乎过分依赖于aeson如何实现事物的具体细节。我认为这是一个问题我错了吗?

  2. 如何根据JSON中存在的键使用正确的数据构造函数?我所有的努力与< |>让我无处可去。

  3. 我会发布到目前为止我写的代码,但我还没有达到我有什么值得发布的地步。

2 个答案:

答案 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