How to work with Haskell keywords in the posts area?

JSON in response to the Github Gists Rest API contains the Haskell keyword type . But typecan not be used as a recording field.

Therefore, it cannot be used in the implementation of Aeson Generic From JSON / 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 suitable approach to deal with such conflicts?

+4
source share
2 answers

We can solve this using TemplateHaskell. Instead of writing ToJSONand FromJONwe can use a specific key mapping.

First of all, we must build a name for a field that is not a type, for example:

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 .

Options: fieldLabelModifier :: String -> String, JSON. , , .

, ownerFieldRename :: String -> String:

ownerFieldRename :: String -> String
ownerFieldRename "owner_type" = "type"
ownerFieldRename name = name

, , "owner_type", "type".

, deriveJSON , :

$(deriveJSON defaultOptions {fieldLabelModifier = ownerFieldRename} ''Owner)

:

RenameUtils.hs

module RenameUtils where

ownerFieldRename :: String -> String
ownerFieldRename "owner_type" = "type"
ownerFieldRename name = name

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)

JSON type:

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 ( ), -:

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)
+5

Willem , , , ToJSON FromJSON ,

data OwnerData = OwnerData {
    oid :: Int
    -- ... other data with non-conflicting names
  } deriving (Show, Generic)

data Owner = Owner {
  owner_data :: OwnerData,
  user_type :: Text
} deriving (Show)

:

-- nothing special for OwnerData: 
instance ToJSON OwnerData
instance FromJSON OwnerData

-- a little helper function to extract the hashmap(Object) from a value
toObject :: ToJSON a => a -> Object
toObject a = case toJSON a of
  Object o -> o
  _        -> error "toObject: value isn't an Object"

-- the instances for Owner
instance ToJSON Owner where
  toJSON (Owner {owner_data = ownerData, user_type = userType}) = 
    Object $ 
    toObject ownerData <> HML.fromList ["type" .= userType]

  toEncoding (Owner {owner_data = ownerData, user_type = userType}) = 
    pairs . foldMap (uncurry (.=)) . HML.toList $ 
    toObject ownerData <> HML.fromList ["type" .= userType]

instance FromJSON Owner where
  parseJSON = withObject "Owner" $ \v -> do
    ownerData <- parseJSON (Object v)
    userType <- v .: "type"
    return Owner { owner_data = ownerData, user_type = userType }

, :

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}

import Data.Aeson
import Data.Text (Text)
import Data.Monoid ((<>))
import GHC.Generics (Generic)
import qualified Data.HashMap.Lazy as HML (fromList, toList)
+2

Source: https://habr.com/ru/post/1692804/


All Articles