Как можно иметь две записи с одинаковыми именами полей?

Я пишу службу JSON для JIRA и столкнулся с требованием, которое конфликтует с пространством имен Haskell. у меня есть эта запись

data Assignee = Assignee {name :: Text} deriving Generic
instance ToJSON Assignee

Это продиктовано тем, что хочет JIRA, к сожалению, она хочет одно и то же поле для другого объекта.

data Reporter = Reporter {name :: Text} deriving Generic
instance ToJSON Reporter

Я вижу несколько вариантов:

  1. Может быть, я могу обойти жалобы компилятора с помощью шаблона Haskell, но как?
  2. Я мог бы просто не иметь записи Reporter и изменить поле Reporter с помощью отдельной службы после создания заявки. Что я знаю, как это сделать, но лучший ли это способ?
  3. Создайте объект JSON вручную, но я формирую его из этой записи:

      data Fields = Fields 
              { project     :: HashMap Key Project
              , summary     :: Text
              , issuetype   :: HashMap Name Task
              , versions    :: [HashMap Name Text]
              , description :: Text
              , assignee    :: Assignee
              } deriving (Generic)
    

Мысль о том, чтобы сделать это вручную, приводит меня в ужас. Если я должен, я буду.

Итак, теперь мой вопрос: если нет другого лучшего способа, чем те, которые я представил, какой из них является лучшим способом действий?


person Michael Litchard    schedule 06.11.2014    source источник
comment
Может быть, вы можете написать их в разных модулях и не импортировать функцию name.   -  person Johannes Kuhn    schedule 07.11.2014


Ответы (2)


Самый простой способ — включить расширение -XDisambiguateRecordFields.

person leftaroundabout    schedule 06.11.2014

Если вам подходит DisambiguateRecordFields и/или хранение записей в отдельных модулях, это отлично.

Если нет, то распространенный способ обойти эту проблему - каким-то образом префиксировать метки полей записи, чтобы устранить неоднозначность:

data Assignee = Assignee {assigneeName :: Text} deriving Generic
data Reporter = Reporter {reporterName :: Text} deriving Generic

Вы по-прежнему можете использовать GHC Generics для получения функций перевода JSON, но вам нужно настроить его так, чтобы метки полей были изменены, например, так:

stripPrefix :: Eq a => [a] -> [a] -> [a]
stripPrefix p x = case splitAt (length p) x of
  (y, z)
    | y == p    -> z
    | otherwise -> x

lower :: String -> String
lower []       = []
lower (x : xs) = toLower x : xs

stripPrefixOptions :: String -> Options
stripPrefixOptions p = defaultOptions {
  fieldLabelModifier = lower . stripPrefix p
}

Тогда вы можете сказать:

data Assignee = Assignee {assigneeName :: Text} deriving Generic
instance ToJSON Assignee where
  toJSON = genericToJSON (stripPrefixOptions "assignee")

data Reporter = Reporter {reporterName :: Text} deriving Generic
instance ToJSON Reporter where
  toJSON = genericToJSON (stripPrefixOptions "reporter")

Тестирование в GHCi:

GHCi> > encode (Assignee { assigneeName = "foo" })
"{\"name\":\"foo\"}"
person kosmikus    schedule 07.11.2014