使用文本键功能性地将序列化中的键名更改为aeson

问题描述 投票:2回答:2

我有一个带有手工制作的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’
json haskell text aeson
2个回答
1
投票

你组成两个函数T.toLowerdrop 3,但类型不匹配。实际上,如果我们查找类型,我们会看到toLower :: Text -> Textdrop :: Int -> [a] -> [a]StringChars的列表,但是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函数,并执行mapping,以便映射字符串中的所有字符。

请注意,如果您只想使用不同的选项派生FromJsonToJSON,您可以使用模板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部分将实现FromJSONToJSON实例。

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

3
投票

你似乎正在使用来自toLowerData.Text,它与Text一起使用,而不是与String一起使用,所以非常自然地,它不适合那里。

相反,你可以在toLower from Data.Char上使用mapString

fieldLabelModifier = map toLower . drop 3
© www.soinside.com 2019 - 2024. All rights reserved.