Заставить ToJSON использовать Show Instance
Если у меня есть тип данных, который выглядит следующим образом:
data SumType = ABC | DEF deriving (Generic, ToJSON)
data MyType = MyType {field1 :: String, field2 :: SumType} deriving (Generic, ToJSON)
Выше будет генерировать JSON, который выглядит следующим образом: {"field1": "blah", "field2":"ABC"}
На практике, MyType
это довольно сложный тип, и я хотел бы сохранить ToJSON
производная, но хочу настроить только одно поле для использования экземпляра show.
instance Show SumType where
show ABC = "abc-blah"
show DEF = "def-xyz"
К сожалению, выше Show
экземпляр не забирается ToJSON
(Я не знаю, должно ли это). Ручной прокат ToJSON
за SumType
кажется, не работает, потому что он ожидает пару ключ-значение (может быть, есть другой способ сделать это?). Другими словами, JSON будет выглядеть так: {"field1": "blah", "field2":{"field3": "ABC"}}
- Я просто хочу изменить способ строкового значения, а не создавать новый объект там.
Любые предложения о том, как я могу изменить строку вывода SumType
без создания вручную ToJSON
за MyType
? Таким образом, выход {"field1": "blah", "field2":"abc-blah"}
Спасибо!
1 ответ
Я не вижу, в чем проблема с определением ToJSON
экземпляр для SumType
, Вы можете сделать это с:
import Data.Aeson(ToJSON(toJSON), Value(String))
import Data.Text(pack)
instance ToJSON SumType where
toJSON = String . pack . show
Или, если вы хотите использовать другие строки для ToJSON
чем Show
:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson(ToJSON(toJSON), Value(String))
instance ToJSON SumType where
toJSON ABC = String "ABC for JSON"
toJSON DEF = String "DEF for JSON"
Теперь Haskell будет JSON-кодировать SumType
в виде строки JSON:
Prelude Data.Aeson> encode ABC
"\"ABC for JSON\""
Prelude Data.Aeson> encode DEF
"\"DEF for JSON\""
Вы можете сделать то же самое с FromJSON
проанализировать строку JSON обратно в SumType
объект:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson(FromJSON(parseJSON), withText)
instance FromJSON SumType where
parseJSON = withText "SumType" f
where f "ABC for JSON" = return ABC
f "DEF for JSON" = return DEF
f _ = fail "Can not understand what you say!"
Если мы затем проанализируем строку JSON, мы получим:
Prelude Data.Aeson> decode "\"ABC for JSON\"" :: Maybe SumType
Just ABC
Prelude Data.Aeson> decode "\"DEF for JSON\"" :: Maybe SumType
Just DEF
Prelude Data.Aeson> decode "\"other JSON string\"" :: Maybe SumType
Nothing
Prelude Data.Aeson> decode "{}" :: Maybe SumType
Nothing
Поэтому, если мы не декодируем строку JSON, которая следует одному из определенных нами шаблонов, синтаксический анализ завершится неудачно. То же самое происходит, если мы не предоставляем строку JSON, а, например, пустой объект JSON.
Дополнительные примечания:
- поскольку
SumType
здесь есть два значения, вы также можете использовать логическое значение JSON для кодирования значений.- Вы также можете кодировать на разных объектах JSON. Например, вы можете использовать строку JSON для
ABC
и целое число дляDEF
, Хотя я бы посоветовал не делать этого, пока нет веских причин (если, например,ABC
несет только строку, иDEF
только целое число).- обычно, чем сложнее кодирование, тем сложнее будет декодирование.