使用文本键在序列化到aeson的功能上更改键名称 [英] Functionally changing key names in serialization to aeson with Text keys
问题描述
我有一个带有手工制作的ToJSON实例的json对象.我想将其替换为不需要我显式枚举键名的函数.
I have a json object with a manually crafted ToJSON instance. I would like to replace this with a function that does not require my explicit enumeration of the key names.
我使用"rec *"作为我要删除的前缀,我的字段开始时是文本"而不是字符串.
I am using "rec*" as a prefix I would like to strip, and my fields start out as Text rather than string.
以最少的数据开始:
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"的每个键名并不是我想要的.
But as you can imagine, typing out every key name by hand from "code" to "recCode" is not something I want to do.
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!
但是,当我尝试使用此功能时,它无法像以前一样自动将文本转换为字符串.
However, when I try this function, it becomes unable to convert the text to string as it had automatically before.
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}’
错误本身非常清楚,以至于Text无法正常工作,但是我应该改变什么以从json输出中的功能名称中删除前缀 并正确地将文本转换为字符串?
The error itself is clear enough that Text doesn't work, but what should I change to strip my prefixes from keynames functionally in json output and also correctly convert text to string?
我也没有更改输入,这让我有些困惑,在两个实例中都是Text类型,但是第一个实现可以使用它,而第二个则不能.
I am also a little confused that I didn't change my input, it was Text type in both instances, but the first implementation was OK to work with it, while the second was not.
我正在使用ihaskell jupyter笔记本.
I am working in an ihaskell jupyter notebook.
当我使用以下答案中推荐的Data.Char时:
When I use the Data.Char recommended in answers below:
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}’
当我尝试裸露的掉落"而不是IHaskellPrelude掉落时,我得到:
And when I try a naked "drop" rather than an IHaskellPrelude drop, I get:
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
,但是类型不匹配.实际上,如果我们查找类型,则会看到 drop :: Int -> [a] -> [a]
. String
是Char
的列表,但Text
不是:Text
可以看作是字符的压缩块".
You compose two function T.toLower
and drop 3
, but the types do not match. Indeed, if we lookup the types, we see toLower :: Text -> Text
and drop :: Int -> [a] -> [a]
. A String
is a list of Char
s, but Text
is not: a Text
can be seen as a packed "block" of characters.
但是,我们可以编写类型为String -> String
的函数,即字段
We can however compose a function of type String -> String
, the type of the field fieldLabelModifier :: String -> String
:
import Data.Char(toLower)
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = map toLower . drop 3
}
因此,我们使用 <Data.Char
模块的strong> toLower :: Char -> Char
函数,并执行map
ping,以便映射字符串中的所有字符.
We thus use the toLower :: Char -> Char
function of the Data.Char
module, and perform a map
ping, such that all characters in the string are mapped.
请注意,如果只想使用不同的选项派生FromJson
和ToJSON
,则可以使用 template Haskell ,例如:
Note that if you simply want to derive FromJson
and ToJSON
with different options, you can make use of template Haskell, like:
{-# 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
实例.
In that case the template Haskell part will implement the FromJSON
and ToJSON
instances.
注意:我们可以使用合格的导入来使我们更清楚地使用什么功能,例如:
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
}
注意:对于智能构造函数,您可以将该表达式简化为:
Note: As for the smart constructor, you can simplify this expression to:
makeR3 = R3
这篇关于使用文本键在序列化到aeson的功能上更改键名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!