我有一个带有手工制作的ToJSON实例的json对象。我想用一个不需要我明确枚举键名的函数替换它。
我使用“rec *”作为我想要删除的前缀,我的字段以Text开头而不是字符串。
从最小数据开始:
data R3 = R3 { recCode :: Code
, recValue :: Value} deriving (Show, Generic)
和智能构造函数:
makeR3 rawcode rawval = R3 code value where
code = rawcode
value = rawval
这个实现工作正常:
instance ToJSON R3 where
toJSON (R3 recCode recValue) = object [ "code" .= recCode, "value" .= recValue]
但是你可以想象,从“代码”到“recCode”手工输入每个关键名称并不是我想做的事情。
tmp_r3 = makeR3 "TD" "100.42"
as_json = encode tmp_r3
main = do
let out = encodeToLazyText tmp_r3
I.putStrLn out
I.writeFile "./so.json" out
return ()
输出正确:
{"value":100.42,"code":"TD"}
-- not recValue and recCode, correct!
但是,当我尝试此功能时,它变得无法将文本转换为字符串,就像之前自动转换一样。
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = T.toLower . IHaskellPrelude.drop 3 }
输出:
<interactive>:8:35: error:
• Couldn't match type ‘Text’ with ‘String’
Expected type: String -> String
Actual type: String -> Text
• In the ‘fieldLabelModifier’ field of a record
In the first argument of ‘genericToJSON’, namely ‘defaultOptions {fieldLabelModifier = toLower . IHaskellPrelude.drop 3}’
In the expression: genericToJSON defaultOptions {fieldLabelModifier = toLower . IHaskellPrelude.drop 3}
<interactive>:8:47: error:
• Couldn't match type ‘String’ with ‘Text’
Expected type: String -> Text
Actual type: String -> String
• In the second argument of ‘(.)’, namely ‘IHaskellPrelude.drop 3’
In the ‘fieldLabelModifier’ field of a record
In the first argument of ‘genericToJSON’, namely ‘defaultOptions {fieldLabelModifier = toLower . IHaskellPrelude.drop 3}’
错误本身足够清楚,文本不起作用,但我应该更改从json输出中功能上从键名中剥离我的前缀,还正确地将文本转换为字符串?
我也有点困惑,我没有改变我的输入,在两个实例中都是Text类型,但第一个实现可以使用它,而第二个不是。
我正在使用ihaskell jupyter笔记本。
当我在下面的答案中使用Data.Char时:
import Data.Char(toLower)
在:
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = Data.Char.toLower . IHaskellPrelude.drop 3 }
我明白了:
<interactive>:8:35: error:
• Couldn't match type ‘Char’ with ‘String’
Expected type: String -> String
Actual type: String -> Char
• In the ‘fieldLabelModifier’ field of a record
In the first argument of ‘genericToJSON’, namely ‘defaultOptions {fieldLabelModifier = Data.Char.toLower . IHaskellPrelude.drop 3}’
In the expression: genericToJSON defaultOptions {fieldLabelModifier = Data.Char.toLower . IHaskellPrelude.drop 3}
<interactive>:8:55: error:
• Couldn't match type ‘String’ with ‘Char’
Expected type: String -> Char
Actual type: String -> String
• In the second argument of ‘(.)’, namely ‘IHaskellPrelude.drop 3’
In the ‘fieldLabelModifier’ field of a record
In the first argument of ‘genericToJSON’, namely ‘defaultOptions {fieldLabelModifier = Data.Char.toLower . IHaskellPrelude.drop 3}’
当我尝试裸露的“掉落”而不是Haskell Prelude掉落时,我得到:
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = Data.Char.toLower . drop 3 }
<interactive>:8:55: error:
Ambiguous occurrence ‘drop’
It could refer to either ‘BS.drop’, imported from ‘Data.ByteString’
or ‘IHaskellPrelude.drop’, imported from ‘Prelude’ (and originally defined in ‘GHC.List’)
or ‘T.drop’, imported from ‘Data.Text’
你组成两个函数T.toLower
和drop 3
,但类型不匹配。实际上,如果我们查找类型,我们会看到toLower :: Text -> Text
和drop :: Int -> [a] -> [a]
。 String
是Char
s的列表,但是Text
不是:Text
可以看作是一个包装好的“块”字符。
然而,我们可以组成String -> String
类型的函数,字段fieldLabelModifier :: String -> String
的类型:
import Data.Char(toLower)
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = map toLower . drop 3
}
因此,我们使用toLower :: Char -> Char
模块的Data.Char
函数,并执行map
ping,以便映射字符串中的所有字符。
请注意,如果您只想使用不同的选项派生FromJson
和ToJSON
,您可以使用模板Haskell,如:
{-# LANGUAGE DeriveGeneric, TemplateHaskell #-}
import Data.Char(toUpper)
import Data.Aeson.TH(deriveJSON, defaultOptions, Options(fieldLabelModifier))
data Test = Test { attribute :: String } deriving Show
$(deriveJSON defaultOptions {fieldLabelModifier = map toUpper . drop 3} ''Test)
在这种情况下,模板Haskell部分将实现FromJSON
和ToJSON
实例。
Note: We can use qualified imports in order to make it more clear what function we use, for example:import qualified Data.List as L import qualified Data.Char as C instance ToJSON R3 where toJSON = genericToJSON defaultOptions { fieldLabelModifier = map C.toLower . L.drop 3 }
注意:对于智能构造函数,您可以将此表达式简化为:
makeR3 = R3
你似乎正在使用来自toLower
的Data.Text
,它与Text
一起使用,而不是与String
一起使用,所以非常自然地,它不适合那里。
相反,你可以在toLower
from Data.Char
上使用map
和String
:
fieldLabelModifier = map toLower . drop 3