使用文本键在功能上将序列化中的键名更改为 aeson
Functionally changing key names in serialization to aeson with Text keys
我有一个 json 对象和一个手动创建的 ToJSON 实例。我想用一个不需要我显式枚举键名的函数来替换它。
我正在使用 "rec*" 作为我想要去除的前缀,并且我的字段开始时是文本而不是字符串。
从最少的数据开始:
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]
但是正如您所想象的那样,手动输入从 "code" 到 "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}’
错误本身很明显,Text 不起作用,但我应该更改什么以在 json 输出中功能性地从键名中删除我的前缀
并正确地将文本转换为字符串?
我也有点困惑,我没有改变我的输入,在这两种情况下都是文本类型,但第一个实现可以使用它,而第二个则不行。
我正在使用 ihaskell jupyter notebook。
更新
当我使用以下答案中推荐的 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}’
当我尝试裸 "drop" 而不是 IHaskellPrelude 下降时,我得到:
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
and drop :: Int -> [a] -> [a]
。 String
是 Char
的列表,但 Text
不是:Text
可以看作是字符的压缩 "block"。
但是我们可以组合一个String -> String
类型的函数,字段的类型fieldLabelModifier :: String -> String
:
import Data.Char(toLower)
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = <b>map toLower</b> . drop 3
}
因此我们使用 Data.Char
模块的 toLower :: Char -> 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 <b>qualified</b> Data.List as <b>L</b>
import <b>qualified</b> Data.Char as <b>C</b>
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = map <b>C.</b>toLower . <b>L.</b>drop 3
}
Note: As for the smart constructor, you can simplify this expression to:
makeR3 = R3
您似乎在使用 Data.Text
中的 toLower
,它适用于 Text
,而不适用于 String
,所以很自然地,它不适合那里。
相反,您可以在 String
上使用 toLower
from Data.Char
和 map
:
fieldLabelModifier = map toLower . drop 3
我有一个 json 对象和一个手动创建的 ToJSON 实例。我想用一个不需要我显式枚举键名的函数来替换它。
我正在使用 "rec*" 作为我想要去除的前缀,并且我的字段开始时是文本而不是字符串。
从最少的数据开始:
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]
但是正如您所想象的那样,手动输入从 "code" 到 "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}’
错误本身很明显,Text 不起作用,但我应该更改什么以在 json 输出中功能性地从键名中删除我的前缀 并正确地将文本转换为字符串?
我也有点困惑,我没有改变我的输入,在这两种情况下都是文本类型,但第一个实现可以使用它,而第二个则不行。
我正在使用 ihaskell jupyter notebook。
更新
当我使用以下答案中推荐的 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}’
当我尝试裸 "drop" 而不是 IHaskellPrelude 下降时,我得到:
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
and drop :: Int -> [a] -> [a]
。 String
是 Char
的列表,但 Text
不是:Text
可以看作是字符的压缩 "block"。
但是我们可以组合一个String -> String
类型的函数,字段的类型fieldLabelModifier :: String -> String
:
import Data.Char(toLower)
instance ToJSON R3 where
toJSON = genericToJSON defaultOptions {
fieldLabelModifier = <b>map toLower</b> . drop 3
}
因此我们使用 Data.Char
模块的 toLower :: Char -> 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 <b>qualified</b> Data.List as <b>L</b> import <b>qualified</b> Data.Char as <b>C</b> instance ToJSON R3 where toJSON = genericToJSON defaultOptions { fieldLabelModifier = map <b>C.</b>toLower . <b>L.</b>drop 3 }
Note: As for the smart constructor, you can simplify this expression to:
makeR3 = R3
您似乎在使用 Data.Text
中的 toLower
,它适用于 Text
,而不适用于 String
,所以很自然地,它不适合那里。
相反,您可以在 String
上使用 toLower
from Data.Char
和 map
:
fieldLabelModifier = map toLower . drop 3