试图用aeson从json中获取一个对象

时间:2017-04-02 22:27:18

标签: haskell aeson

我正在尝试从以下代码中获取Config数据记录:

data Connections = Connections { cfgProperty  :: !Object
                   , connectionName :: String
                   } deriving (Show, Generic)

data Config = Config {connections :: [Connections]} deriving (Show, Generic)

data Cfg = Cfg { config :: Config } deriving (Show, Generic)



instance FromJSON Cfg
instance FromJSON Config
instance FromJSON Connections
instance ToJSON Cfg
instance ToJSON Config
instance ToJSON Connections

jsonFile :: FilePath
jsonFile = "config/config.json"

getCfg :: IO B.ByteString
getCfg = B.readFile jsonFile


parseCfg = do
      j <- (A.eitherDecode <$> getCfg) :: IO (Either String Cfg)
      case j of
        Left err ->  liftIO $ putStrLn err
        Right j -> config j

我收到以下错误:

/apps/workspace/hade/src/Actor/MasterActor.hs: 56, 20
• Couldn't match expected type ‘IO ()’ with actual type ‘Config’
• In the expression: config j
  In a case alternative: Right j -> config j
  In a stmt of a 'do' block:
    case j of {
      Left err -> liftIO $ putStrLn err
      Right j -> config j }

这是config.json

{
  "config":
  {
    "connections": [
    {
      "cfgProperty":
      {
        "Env": "local",
        "Host": "localhost",
        "Port": "8001",
        "Directory": "/apps/workspace/hade/maps/src01_cvs"
      },
      "connectionName": "src01_cvs"
    },
    {
        "cfgProperty":
        {
          "Env": "local",
          "Host": "localhost",
          "Port": "8001",
          "Directory": "/apps/workspace/hade/maps/trg01_cvs"
        },
        "connectionName": "trg01_cvs"
      }
    ]
  }
}{
  "config":
  {
    "connections": [
    {
      "cfgProperty":
      {
        "Env": "local",
        "Host": "localhost",
        "Port": "8001",
        "Directory": "/apps/workspace/hade/maps/src01_cvs"
      },
      "connectionName": "src01_cvs"
    },
    {
        "cfgProperty":
        {
          "Env": "local",
          "Host": "localhost",
          "Port": "8001",
          "Directory": "/apps/workspace/hade/maps/trg01_cvs"
        },
        "connectionName": "trg01_cvs"
      }
    ]
  }
}

我已经使用Decode和decode尝试了许多不同的配置,但每次都遇到了障碍。如果我将案例更改为:

,我可以获取打印出Config记录的代码
      case j of
        Left err ->  putStrLn err
        Right j -> print $ config j

(以及其他一些更改),但我无法简单地返回Config记录本身。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您遇到的问题的根源是getCfg中对parseCfg的调用。 getCfg的类型是IO ByteString,它是IO monad,你可以用IO monad做的唯一事情是调用必须为所有人定义的函数monads(bindfmapap,...)所有这些都会返回另一个IO monad。这意味着如果parseCfg要调用getCfg,那么必须返回IO monad。

引用haskell wiki:“因为你无法从IO monad中逃脱,所以不可能编写一个在IO monad中进行计算但其结果类型不包含{{1类型构造函数。“

解决此问题的一种方法是在IO之外拨打getCfg并将结果传递给parseCfg。然后parseCfg 可以返回parseCfg但返回Config更有意义,以便保留Either String Config的任何解析错误。这样,您就可以将eitherDecode定义为parseCfg fmap的{​​{1}}返回值。

结果函数如下所示:

config

以下是您的程序,已修改为使用上述eitherDecode运行。

parseCfg :: ByteString -> Either String Config
parseCfg json = config <$> eitherDecode json 

这是经过修改的parseCfg。原始文件包含两个{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TemplateHaskell #-} import Data.ByteString.Lazy as B import Data.Aeson import Data.Aeson.TH import GHC.Generics import Control.Monad.IO.Class data CfgProperty = CfgProperty { env :: String, host :: String, port :: String, directory :: String } deriving (Show, Generic) -- Instead of FromJSON, use deriveJSON for CfgProperty -- which allows changing of field labels from lower -- case to upper case. $(deriveJSON defaultOptions { fieldLabelModifier = let f "env" = "Env" f "host" = "Host" f "port" = "Port" f "directory" = "Directory" f other = other in f } ''CfgProperty ) data Connections = Connections { cfgProperty :: CfgProperty , connectionName :: String } deriving (Show, Generic) data Config = Config {connections :: [Connections]} deriving (Show, Generic) data Cfg = Cfg { config :: Config } deriving (Show, Generic) instance FromJSON Cfg instance FromJSON Config instance FromJSON Connections jsonFile :: FilePath jsonFile = "config/config.json" getCfg :: IO ByteString getCfg = B.readFile jsonFile parseCfg :: ByteString -> Either String Config parseCfg json = config <$> eitherDecode json main :: IO() main = do json <- getCfg case parseCfg json of Left err -> Prelude.putStrLn err Right cfg -> print cfg -- do whatever you want with cfg here. config.json条记录,它们之间没有任何内容,这些记录无效config,与问题无关。

JSON