将Haskell与Aeson JSON Hackage一起使用,并给出以下JSON:
{
"base": "GBP",
"date": "2017-10-27",
"rates": {
"#USD": 1.3093,
"#EUR": 1.1282
}
}
实施FromJson
实例的最佳方式是什么?
目前我有这个:
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
import GHC.Generics
import Data.Aeson
data Conversion = Conversion {
base :: String,
rates :: Rates }
deriving (Show, Generic)
data Rates = Rates {
eur :: Float,
usd :: Float }
deriving (Show, Generic)
instance FromJSON Conversion
instance FromJSON Rates where
parseJSON (Object o) = trace ( show(o)) Rates <$> o .: "#USD" <*> o .: "#EUR"
我在instance FromJSON Rates
中定义了两种可能性。我尝试用更通用的方式来做,但是非法的&#39; #
部分中不允许使用字符data
。
所以在这种情况下我只有两个烦人的领域。但是如果我想扩展它并获得多个讨厌的字符(#,@, - 等),我是否必须定义每个字段?或者有更智能,更快捷的方法来实现同样的目标吗?
答案 0 :(得分:1)
您可以使用fieldLabelModifier
并使用您自己的字段替换有问题的字段来处理此问题。这使您可以选择修改哪些名称,如果您的大型记录只有几个奇怪命名的字段,而这些字段无法直接放入您的类型中,那么这些名称非常有用。
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Data.Aeson
import Data.Aeson.Types
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Map.Strict as M
import GHC.Generics
import System.Environment (getArgs)
data Conversion = Conversion
{ base :: String
, rates :: Rates
} deriving (Show, Generic)
newtype USD = USD Float
newtype EUR = EUR Float
data Rates = Rates
{ eur :: Float
, usd :: Float
}
deriving (Show, Generic)
instance FromJSON Conversion
instance FromJSON Rates where
parseJSON = genericParseJSON opts
where
fields = M.fromList
[("usd", "#USD"), ("eur", "#EUR")]
opts = defaultOptions
{ fieldLabelModifier = \s -> M.findWithDefault s s fields }
main :: IO ()
main = do
[file] <- getArgs
decode <$> BSL.readFile file >>= \case
Nothing -> putStrLn "Parse failed!"
Just conversion -> print (conversion :: Conversion)
有了这个我们得到
[nix-shell:/tmp]$ ./T /tmp/rates.json
Conversion {base = "GBP", rates = Rates {eur = 1.1282, usd = 1.3093}}
[nix-shell:/tmp]$ cat /tmp/rates.json
{
"base": "GBP",
"date": "2017-10-27",
"rates": {
"#USD": 1.3093,
"#EUR": 1.1282
}
}
如果您为类型定义ToJSON
个实例,请记住使用相同的Aeson选项!