Decoding object with multiple constuctors with separated tags

江枫思渺然 提交于 2020-04-16 03:27:07

问题


I have data in form of pairs of two strings, where first is the key identifying shape of the JSON delivered as the second one.

fooooo
{"a": 123}

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

fooooo
{"a": 123}

I would like to parse it to same data type, based on the fopooo, baasdasda1, etc :

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

In the Aeson there is a tag feature, but it seems to require presence of the tag inside the object.

Is there some way to handle it like (with tag outside)

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

Or something similar to it?

edit1

I've currenlty mananged to handle types with records by this

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)])

(Issue is if thee are no constructor params :( )


回答1:


You can use the UntaggedValue option:

{-# 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

As the documentation explains about UntaggedValue:

When decoding, constructors are tried in the order of definition. If some encodings overlap, the first one defined will succeed.

Demo:

*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})

That said, you shouldn't model sum types with records, because the record accessors are partial:

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


来源:https://stackoverflow.com/questions/59916344/decoding-object-with-multiple-constuctors-with-separated-tags

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!