问题
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