如何用aeson解析嵌套的JSON

luk*_*all 14 json haskell

我试图使用aeson解析以下形式的JSON

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

or

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

or

{"line":{"number":"..."}}
Run Code Online (Sandbox Code Playgroud)

构造以下数据类型

data Rule = Line Integer
          | Field L.ByteString
          | Tag L.ByteString
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

hvr*_*hvr 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"
Run Code Online (Sandbox Code Playgroud)


Pet*_*lák 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"
Run Code Online (Sandbox Code Playgroud)

并使用它嵌入对象的两个函数:

(.:*) :: (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
Run Code Online (Sandbox Code Playgroud)

lookupE取决于内部表示,因此如果更改,则很容易对其进行修改.然后{"tag":{"name":"..."}}被解析为v .:* ["tag", "name"].请注意,它也适用于空列表 - v .:* []相当于parseJSON v.