2017-04-02 59 views
1

我想獲得一個配置數據記錄了下面的代碼來獲取對象了JSON的:嘗試使用埃宋

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" 
     } 
    ] 
    } 
} 

我已經嘗試了許多不同的配置使用bothDecode和解碼,但我碰到路由器每次鎖定。我可以得到代碼打印出來的配置記錄,如果我改變的情況是:

 case j of 
     Left err -> putStrLn err 
     Right j -> print $ config j 

(連同一些其他的變化),但我不能把它簡單地返回配置記錄本身。任何援助將不勝感激。

+1

這是一個提示:'case'語句的每個分支必須返回相同的類型。另外,'do'塊的類型與上一個表達式相同。 「parseCfg」的類型應該是什麼?如果您在所有頂級聲明中放置類型註釋(無論如何,您應該這樣做),那麼解決問題的方法應該會變得更加清晰。 –

+0

是否返回$ config j工作? – mac10688

+0

亞歷克西斯,我可以從「左 - >」返回一個空的Config記錄,以使類型相同,但我不知道我該怎麼做。有任何想法嗎?我嘗試了一些不同的東西,但是這一點沒有成功。 – peteofce

回答

1

您遇到的問題的根源是撥打getCfgparseCfg。該類型的getCfgIO ByteString這是一個IO單子,唯一的東西,你可以用IO單子做都呼籲必須爲所有的單子被定義的函數(bindfmapap,...),所有這些回報另一個IO monad。這意味着如果parseCfg打算撥打getCfg那麼它必須返回一個IO單子。

引用haskell wiki:「因爲不能從IO monad中退出,所以不可能編寫一個在IO monad中執行計算但其結果類型不包含IO類型構造函數的函數。

解決此問題的方法之一是致電parseCfg之外的getCfg,並將結果傳遞給parseCfg。然後parseCfg可能返回Config但它更有意義返回Either String Config,以便從eitherDecode保留任何解析錯誤。這使您可以在eitherDecode返回值上將parseCfg定義爲fmapconfig

產生的功能如下:

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。原始文件中包含兩個configJSON記錄,但它們之間沒有任何內容,這些記錄無效,並且與該問題無關。

{ 
    "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" 
     } 
    ] 
    } 
}