在 Haskell 中使用 Aeson 解析 JSON 配置文件

克里斯托夫

我正在尝试在 Haskell 中解析 JSON,但找不到有关此任务的任何有用文档。我无法使以下小程序工作:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.Word (Word16)
import Data.ByteString.Lazy (pack, ByteString)
import Data.ByteString.Char8 ()

data AppConfig = AppConfig {
    database :: DatabaseConfig
} deriving (Show)

data DatabaseConfig = DatabaseConfig {
    host :: String,
    port :: Word,
    username :: String,
    password :: String,
    databaseName :: String
} deriving (Show)

instance FromJSON DatabaseConfig where
    parseJSON (Object obj) = DatabaseConfig
                <$> obj .: "host"
                <*> obj .: "port"
                <*> obj .: "username"
                <*> obj .: "password"
                <*> obj .: "databaseName"
    parseJSON obj = fail $ show obj

instance FromJSON AppConfig where
    parseJSON (Object obj) = AppConfig <$> obj .: "database"
    parseJSON obj = fail $ show obj

config = "{ \"database\": { \"host\": \"db\", \"port\": 1234, \"username\": \"ledger\",  \"password\": \"ledger\", \"database_name\": \"ledger\" } }\""

main :: IO ()
main = do
  let cfg = eitherDecode config
  case cfg of
    Left err -> fail $ "error: " ++ err
    Right ps -> putStrLn $ ps

错误消息完全没有意义:

用户错误(错误:$ 中的错误:endOfInput)

那么代码有什么问题呢?

打字机

如果您查看packfrom的类型签名Data.ByteString

pack :: [Word8] -> ByteString

你看,它不需要一个String或某种类型的实现IsString类型类,而是一个字节列表。让我们通过使用Data.ByteString.Char8代替来解决这个问题Data.ByteString

现在,出现以下错误:

test.hs:36:12: error:
    • Couldn't match type ‘Either String’ with ‘IO’
      Expected type: IO (Either [Char] String)
        Actual type: Either String (Either [Char] String)
    • In a stmt of a 'do' block: cfg <- (eitherDecode config)
      In the expression:
        do cfg <- (eitherDecode config)
           case cfg of
             Left err -> fail $ "error: " ++ err
             Right ps -> putStrLn $ ps
      In an equation for ‘main’:
          main
            = do cfg <- (eitherDecode config)
                 case cfg of
                   Left err -> fail $ "error: " ++ err
                   Right ps -> putStrLn $ ps
   |
36 |   cfg <-  (eitherDecode config)
   |            ^^^^^^^^^^^^^^^^^^^

那是因为eitherDecode config不会导致IO a某个 type 的 typea值,而是type的值Either String a所以让我们解决这个问题。

现在出现以下错误:

 test.hs:36:27: error:
    • Couldn't match expected type ‘Data.ByteString.Lazy.Internal.ByteString’
                  with actual type ‘ByteString’
      NB: ‘ByteString’ is defined in ‘Data.ByteString.Internal’
          ‘Data.ByteString.Lazy.Internal.ByteString’
            is defined in ‘Data.ByteString.Lazy.Internal’
    • In the first argument of ‘eitherDecode’, namely ‘config’
      In the expression: (eitherDecode config)
      In an equation for ‘cfg’: cfg = (eitherDecode config)
   |
36 |   let cfg = (eitherDecode config)
   |                           ^^^^^^

显然我们选错了ByteString类型。ByteStringfromData.ByteString.Char8是严格的,但 aeson 需要惰性字节串。所以让我们通过使用来解决这个问题Data.ByteString.Lazy.Char8

现在它编译并运行程序会出现以下错误:

[nix-shell:~/tmp]$ ./test
test: user error (error: Error in $: endOfInput)

那是因为您的字符串文字中的 JSON 是错误的。它读

"{ \"database\": { \"host\": \"db\", \"port\": 1234, \"username\": \"ledger\",  \"password\": \"ledger\", \"database_name\": \"ledger\" } }\""

它应该是:

"{ \"database\": { \"host\": \"db\", \"port\": 1234, \"username\": \"ledger\",  \"password\": \"ledger\", \"database_name\": \"ledger\" } }"

现在重新编译和运行测试会导致以下错误:

[nix-shell:~/tmp]$ ./test
test: user error (error: Error in $: expected String, encountered Object)

所以很明显aeson认为它应该解码一个json字符串值,但遇到了一个对象。如果您查看以下几行

let cfg = (eitherDecode config)
case cfg of
   Left err -> fail $ "error: " ++ err
   Right ps -> putStrLn $ ps

你看,这ps被输入为String因为它被用作 的参数putStrLn,这解释了观察到的行为。如果我们只是换putStrLn $ psputStrLn $ show ps了编译器不知道在所有的,什么类型的ps可能是,让我们帮助他的类型注释。

现在重新编译和运行测试会导致以下错误:

[nix-shell:~/tmp]$ ./test
test: user error (error: Error in $.database: key "databaseName" not present)

所以你的程序期望databaseName作为 json 对象中的键,而不是database_name. 修复FromJSON实例以解决该问题。

现在它输出:

[nix-shell:~/tmp]$ ./test
AppConfig {database = DatabaseConfig {host = "db", port = 1234, username = "ledger", password = "ledger", databaseName = "ledger"}}

最终的程序现在是:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.Word (Word16)
import Data.ByteString.Lazy.Char8 (pack,ByteString)

data AppConfig = AppConfig {
    database :: DatabaseConfig
} deriving (Show)

data DatabaseConfig = DatabaseConfig {
    host :: String,
    port :: Word,
    username :: String,
    password :: String,
    databaseName :: String
} deriving (Show)

instance FromJSON DatabaseConfig where
    parseJSON (Object obj) = DatabaseConfig
                <$> obj .: "host"
                <*> obj .: "port"
                <*> obj .: "username"
                <*> obj .: "password"
                <*> obj .: "database_name"
    parseJSON obj = fail $ show obj

instance FromJSON AppConfig where
    parseJSON (Object obj) = AppConfig <$> obj .: "database"
    parseJSON obj = fail $ show obj

config = pack "{ \"database\": { \"host\": \"db\", \"port\": 1234, \"username\": \"ledger\",  \"password\": \"ledger\", \"database_name\": \"ledger\" } }"

main :: IO ()
main = do
  let cfg = (eitherDecode config) :: Either String AppConfig
  case cfg of
    Left err -> fail $ "error: " ++ err
    Right ps -> putStrLn $ show ps

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何使用Haskell Aeson解析数组

来自分类Dev

Haskell :: Aeson ::根据字段值解析ADT

来自分类Dev

用lens-aeson解析json

来自分类Dev

Haskell Aeson JSON库ByteString问题

来自分类Dev

解析配置文件

来自分类Dev

如何使用R解析INI之类的配置文件?

来自分类Dev

如何使用Aeson解析Bitly Response JSON?

来自分类Dev

使用Pipes.Aeson在Haskell中进行JSON的流解析

来自分类Dev

忽略Haskell Aeson输入中的JSON字段

来自分类Dev

如何使用Aeson解析整数

来自分类Dev

使用aeson解析JSON以获取复合数据类型

来自分类Dev

在Haskell中使用Cassava解析文件时创建列表

来自分类Dev

如何使用Aeson解析此JSON?

来自分类Dev

使用Aeson解析JSON文档时解析引用

来自分类Dev

在Django中使用Python解析JSON文件

来自分类Dev

Aeson使用先前解析的默认值解析JSON

来自分类Dev

如何在Haskell / Aeson中使用类型函数解析多态值?

来自分类Dev

使用R或AWK解析类似JSON的配置文件

来自分类Dev

忽略Haskell Aeson输入中的JSON字段

来自分类Dev

如何使用Aeson解析整数

来自分类Dev

使用aeson解析JSON以获取复合数据类型

来自分类Dev

使用Aeson从Haskell中的URL解析JSON数据

来自分类Dev

Haskell aeson ParseJSON示例使用(。:)

来自分类Dev

使用Aeson解析JSON文档时解析引用

来自分类Dev

Json解析用户配置文件提要中面临的问题

来自分类Dev

Haskell Aeson嵌套数组JSON

来自分类Dev

Haskell Aeson - Gloss - JSON 实例解析器:颜色

来自分类Dev

使用 Haskell Aeson 解析 Json 玫瑰树

来自分类Dev

使用 Aeson 解析嵌入的 JSON