Декодирование объекта с несколькими конструкторами с разделенными тегами - PullRequest
1 голос
/ 26 января 2020

У меня есть данные в виде пар из двух строк, где первая - это ключ, идентифицирующий форму JSON, доставленного как вторая.

fooooo
{"a": 123}

barrrr
{"a": 123, "b": 123}

fooooo
{"a": 123}

Я хотел бы проанализировать его для тех же данных тип на основе fopooo, baasdasda1, и т. д. c:

data Test
  = Foo
    { a :: Int
    , b :: Int
    }
  | Bar
    { a :: Int
    }
  deriving (Show, Generic)

В Aeson есть функция тега , но она Кажется, требуется наличие тега внутри объекта.

Есть ли способ справиться с этим, например (с тегом снаружи)

magicDecode :: String -> Test
magicDecode "fooooo" = decodeSpecyfic Foo
magicDecode "barrrr" = decodeSpecyfic Bar

Или что-то похожее на него ?

edit1

У меня есть множество функций для обработки типов с записями этим

test :: [(String, Text)]
test =
  [ ("foooo", "Foo")
  , ("barrr", "Bar")
  , ("aaaa", "Lel")
  ]

dec :: String -> ByteString -> Test
dec t x =
  fromJust $
  parseMaybe
    (genericParseJSON defaultOptions {sumEncoding = ObjectWithSingleField})
    (object [fromJust (lookup t test) .= fromJust (decode x :: Maybe Value)])

(Проблема в том, что если у вас нет параметров конструктора :()

1 Ответ

2 голосов
/ 26 января 2020

Вы можете использовать опцию UntaggedValue:

{-# LANGUAGE DeriveGeneric #-}
module Q59916344 where

import Data.Aeson
import GHC.Generics

myOptions = defaultOptions { sumEncoding = UntaggedValue }

data Test = Foo { a :: Int, b :: Int } | Bar { a :: Int } deriving (Show, Generic)

instance FromJSON Test where
  parseJSON = genericParseJSON myOptions

instance ToJSON Test where
  toJSON     = genericToJSON myOptions
  toEncoding = genericToEncoding myOptions

Как документация объясняет UntaggedValue:

При декодировании конструкторы попробовал в порядке определения. Если некоторые кодировки перекрываются, первое определенное будет успешным.

Демонстрация:

*Q59916344 Q59916344> decode "{\"a\": 123}" :: Maybe Test
Just (Bar {a = 123})
*Q59916344 Q59916344> decode "{\"a\": 123, \"b\": 123}" :: Maybe Test
Just (Foo {a = 123, b = 123})

При этом вы не должны моделировать типы сумм с записями, потому что записи доступа являются частичными:

*Q59916344 Q59916344> b (Foo 42 1337)
1337
*Q59916344 Q59916344> b (Bar 42)
*** Exception: No match in record selector b
...