使用文本键在序列化到aeson的功能上更改键名称 [英] Functionally changing key names in serialization to aeson with Text keys

查看:98
本文介绍了使用文本键在序列化到aeson的功能上更改键名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有手工制作的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.toLowerdrop 3,但是类型不匹配.实际上,如果我们查找类型,则会看到 drop :: Int -> [a] -> [a] . StringChar的列表,但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 Chars, 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 mapping, such that all characters in the string are mapped.

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆