尝试使用 aeson 从 json 中取出一个对象
Trying to get an object out of json using aeson
我正在尝试从以下代码中获取配置数据记录:
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"
}
]
}
}
我尝试了很多不同的配置,同时使用解码和解码,但我每次都 运行 遇到障碍。如果我将大小写更改为:
,我可以获得打印配置记录的代码
case j of
Left err -> putStrLn err
Right j -> print $ config j
(以及其他一些更改),但我无法简单地 return 配置记录本身。如有任何帮助,我们将不胜感激。
您 运行 遇到的问题的根源在于 parseCfg
中对 getCfg
的调用。 getCfg
的类型是 IO ByteString
,它是一个 IO
monad,你可以用 IO
monad 做的唯一事情就是调用必须为所有定义的函数monads (bind
, fmap
, ap
, ...) 所有这些 return 另一个 IO
monad。这意味着如果 parseCfg
要调用 getCfg
那么它 必须 return 一个 IO
monad.
引用 haskell wiki : "Because you can't escape from the IO monad, it is impossible to write a function that does a computation in the IO monad but whose result type does not include the IO
type constructor."
解决此问题的一种方法是在 parseCfg
之外调用 getCfg
并将结果传递给 parseCfg
。然后 parseCfg
可以 return 一个 Config
但是 return Either String Config
更有意义,这样来自的任何解析错误eitherDecode
被保留。这让您可以将 parseCfg
定义为 config
的 fmap
超过 eitherDecode
return 值。
生成的函数如下所示:
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
记录,它们之间没有任何内容,这是无效的 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"
}
]
}
}
我正在尝试从以下代码中获取配置数据记录:
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"
}
]
}
}
我尝试了很多不同的配置,同时使用解码和解码,但我每次都 运行 遇到障碍。如果我将大小写更改为:
,我可以获得打印配置记录的代码 case j of
Left err -> putStrLn err
Right j -> print $ config j
(以及其他一些更改),但我无法简单地 return 配置记录本身。如有任何帮助,我们将不胜感激。
您 运行 遇到的问题的根源在于 parseCfg
中对 getCfg
的调用。 getCfg
的类型是 IO ByteString
,它是一个 IO
monad,你可以用 IO
monad 做的唯一事情就是调用必须为所有定义的函数monads (bind
, fmap
, ap
, ...) 所有这些 return 另一个 IO
monad。这意味着如果 parseCfg
要调用 getCfg
那么它 必须 return 一个 IO
monad.
引用 haskell wiki : "Because you can't escape from the IO monad, it is impossible to write a function that does a computation in the IO monad but whose result type does not include the IO
type constructor."
解决此问题的一种方法是在 parseCfg
之外调用 getCfg
并将结果传递给 parseCfg
。然后 parseCfg
可以 return 一个 Config
但是 return Either String Config
更有意义,这样来自的任何解析错误eitherDecode
被保留。这让您可以将 parseCfg
定义为 config
的 fmap
超过 eitherDecode
return 值。
生成的函数如下所示:
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
记录,它们之间没有任何内容,这是无效的 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"
}
]
}
}