如何处理记录字段中Haskell的保留关键字? [英] How to deal with Haskell's reserved keywords in record fields?
问题描述
JSON 包含 Haskell的关键字 type
.但是type
不能用作记录字段.
JSON in response of Github Gists Rest API contains Haskell's keyword type
. But type
couldn't be used as a record field.
因此,它不能用于实现 Aeson的通用 FromJSON/ToJSON实例.
Thus it couldn't be used in implementation of Aeson's Generic FromJSON/ToJSON instances.
import Data.Text (Text)
import GHC.Generics (Generic)
type URL = Text
data OwnerType = User deriving (Show)
data Owner = Owner {
id :: Int,
gravatar_id :: Text,
login :: Text,
avatar_url :: Text,
events_url :: URL,
followers_url :: URL,
following_url :: URL,
gists_url :: URL,
html_url :: URL,
organizations_url :: URL,
received_events_url :: URL,
repos_url :: URL,
starred_url :: URL,
subscriptions_url :: URL,
url :: URL,
-- type :: Text,
site_admin :: Bool
} deriving (Generic, Show)
instance ToJSON Owner
instance FromJSON Owner
问题: 是否有解决此类冲突的适当方法?
Question: Is there a proper approach to deal with such kind of conflicts?
推荐答案
我们可以使用TemplateHaskell
解决此问题.代替编写ToJSON
和FromJON
,我们可以使用键的特定映射.
We can solve this by using TemplateHaskell
. Instead of writing ToJSON
and FromJON
, we can use a specific mapping of the keys.
首先,我们必须为不是 类型的字段构造一个名称,例如:
First of all, we have to construct a name for the field that is not type, for instance:
data Owner = Owner {
id :: Int,
gravatar_id :: Text,
login :: Text,
avatar_url :: Text,
events_url :: URL,
followers_url :: URL,
following_url :: URL,
gists_url :: URL,
html_url :: URL,
organizations_url :: URL,
received_events_url :: URL,
repos_url :: URL,
starred_url :: URL,
subscriptions_url :: URL,
url :: URL,
owner_type :: Text,
site_admin :: Bool
} deriving (Generic, Show)
现在,我们可以使用 deriveJSON :: Options -> Name -> Q [Dec]
函数,该函数将构造一个fromJSON
和toJSON
实例.
Now we can use the deriveJSON :: Options -> Name -> Q [Dec]
function that will construct a fromJSON
and toJSON
instance.
此处的关键是 Options
参数:它包含一个 fieldLabelModifier :: String -> String
字段,该字段可以将字段名称重写为JSON中的键.因此,我们可以在此处生成一个将其重写的函数.
The key here is the Options
parameter: it contains a fieldLabelModifier :: String -> String
field that can rewrite the names of the fields to the keys in JSON. We can thus here generate a function that will rewrite it.
所以我们首先构造一个函数ownerFieldRename :: String -> String
:
So we first construct a function ownerFieldRename :: String -> String
:
ownerFieldRename :: String -> String
ownerFieldRename "owner_type" = "type"
ownerFieldRename name = name
因此,除了映射到"type"
上的"owner_type"
之外,此函数还用作标识函数.
So this function acts as an identity function, except for "owner_type"
, which is mapped on "type"
.
所以现在我们可以使用自定义选项调用deriveJSON
函数:
So now we can call the deriveJSON
function with custom options like:
$(deriveJSON defaultOptions {fieldLabelModifier = ownerFieldRename} ''Owner)
或全部:
RenameUtils.hs
:
RenameUtils.hs
:
module RenameUtils where
ownerFieldRename :: String -> String
ownerFieldRename "owner_type" = "type"
ownerFieldRename name = name
MainFile.hs
:
MainFile.hs
:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson.TH(deriveJSON, defaultOptions, Options(fieldLabelModifier))
import RenameUtils(ownerFieldRename)
import Data.Text (Text)
type URL = Text
data Owner = Owner {
id :: Int,
gravatar_id :: Text,
login :: Text,
avatar_url :: Text,
events_url :: URL,
followers_url :: URL,
following_url :: URL,
gists_url :: URL,
html_url :: URL,
organizations_url :: URL,
received_events_url :: URL,
repos_url :: URL,
starred_url :: URL,
subscriptions_url :: URL,
url :: URL,
owner_type :: Text,
site_admin :: Bool
} deriving (Show)
$(deriveJSON defaultOptions {fieldLabelModifier = ownerFieldRename} ''Owner)
现在,我们获得一个以type
作为键的JSON对象:
Now we obtain a JSON object with type
as key:
Prelude Main Data.Aeson> encode (Owner 1 "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" True)
"{\"id\":1,\"gravatar_id\":\"\",\"login\":\"\",\"avatar_url\":\"\",\"events_url\":\"\",\"followers_url\":\"\",\"following_url\":\"\",\"gists_url\":\"\",\"html_url\":\"\",\"organizations_url\":\"\",\"received_events_url\":\"\",\"repos_url\":\"\",\"starred_url\":\"\",\"subscriptions_url\":\"\",\"url\":\"\",\"type\":\"\",\"site_admin\":true}"
对于简单的fieldLabelModifier
函数,我们不需要编写特定的函数(必须在特定的模块中定义),我们还可以在此处使用 lambda表达式:
For a simple fieldLabelModifier
function we do not need to write a specific function (that we have to define in a specific module), we can also use an lambda expression here:
MainFile.hs
:
MainFile.hs
:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson.TH(deriveJSON, defaultOptions, Options(fieldLabelModifier))
import Data.Text (Text)
type URL = Text
data Owner = Owner {
id :: Int,
gravatar_id :: Text,
login :: Text,
avatar_url :: Text,
events_url :: URL,
followers_url :: URL,
following_url :: URL,
gists_url :: URL,
html_url :: URL,
organizations_url :: URL,
received_events_url :: URL,
repos_url :: URL,
starred_url :: URL,
subscriptions_url :: URL,
url :: URL,
owner_type :: Text,
site_admin :: Bool
} deriving (Show)
$(deriveJSON defaultOptions {fieldLabelModifier = \x -> if x == "owner_type" then "type" else x} ''Owner)
这篇关于如何处理记录字段中Haskell的保留关键字?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!