Haskell爱生JSON,过滤掉非法字符

Haskell Aeson JSON, filter out illegal characters

将 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 中定义了两种可能性。我尝试用更通用的方式来做到这一点,但是 'illegal' 字符 #data 部分中是不允许的。

所以在这种情况下,我只有两个烦人的字段。但是如果我想扩展它并获得多个烦人的字符(#、@、- 等),我是否必须定义每个字段?还是有更聪明、更快捷的方法来实现同样的目标?

您可以使用 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 选项!