Заставить 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.

Дополнительные примечания:

  1. поскольку SumType здесь есть два значения, вы также можете использовать логическое значение JSON для кодирования значений.
  2. Вы также можете кодировать на разных объектах JSON. Например, вы можете использовать строку JSON для ABCи целое число для DEF, Хотя я бы посоветовал не делать этого, пока нет веских причин (если, например, ABC несет только строку, и DEF только целое число).
  3. обычно, чем сложнее кодирование, тем сложнее будет декодирование.
Другие вопросы по тегам