Как сериализовать тип данных суммы записей в Aeson без тега?

У меня есть тип данных с несколькими конструкторами, например

data AB = A 
  { 
    ab :: Text
  , a :: Text
  } 
  | B 
  {
    ab :: Text
  , b :: Text
  } deriving (Generic)

Прямо сейчас, когда я использую Aeson для сериализации A он сгенерирован для следующего JSON:

{
  "tag": "A",
  "ab": "some text",
  "a": "some text"
}

Я знаю что можно использовать SumEncoding манипулировать, как конструктор будет обрабатываться, но не смог найти то, что я хочу.

Можно ли как-то опустить tag поле в сериализованном JSON? Мне нужен только один способ сериализации (нет причин сохранять десериализацию), но тип данных довольно большой, чтобы написать, как сериализовать его вручную.

3 ответа

Решение

Хакерский способ - просто удалить tag из полученного объекта:

{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson
import Data.HashMap.Strict
import Data.String
import Data.Text
import GHC.Generics

data AB
  = A { ab :: Text
      , a  :: Text
      }
  | B { ab :: Text
      , b  :: Text
      }
  deriving Generic

instance ToJSON AB where
  toJSON ab = case genericToJSON defaultOptions ab of
    Object o -> Object (delete (fromString "tag") o)
    _ -> error "impossible"

Вероятно, он вернется, чтобы укусить вас, когда родовой экземпляр неизбежно изменится, поэтому, если вы сделаете это, осознайте технический долг, который вы решили взять на себя.

Options имеет sumEncoding поле и defaultOptions устанавливает это defaultTaggedObject,

Установка его в UntaggedValue (от SumEncoding) следует делать то, что вы ищете.

Просто определите ToJSON пример себя:

instance ToJSON AB where
    toJSON (A ab a) = object [ "ab" .= ab, "a" .= a ]
    toJSON (B ab a) = object [ "ab" .= ab, "a" .= a ]

ab1 = A "foo" "bar"
ab2 = A "abc" "def"

*Main> import qualified Data.ByteString.Lazy.Char8 as LBS
*Main LBS> LBS.putStrLn $ encode ab2
{"ab":"abc","a":"def"}
*Main LBS> LBS.putStrLn $ encode ab1
{"ab":"foo","a":"bar"}
Другие вопросы по тегам