How to write a ToJSON / FromJSON instance for a data type with multiple constructors?

Each example that I saw for ToJSONand FromJSONrelates to data types with single constructors, for example:

data RewindConfig = RConfig JobID Phase
                      deriving Show

instance FromJSON RewindConfig where
  parseJSON (Object o) = RConfig
    <$> o .: "JobID"
    <*> o .: "Phase"
  parseJSON _ = fail "invalid RewindConfig"

I thought I'd see how Aeson creates an instance for a type with multiple constructors, for example Either:

instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
   parseJSON (Object (H.toList -> [(key, value)]))
        | key == left  = Left  <$> parseJSON value
        | key == right = Right <$> parseJSON value
   parseJSON _        = fail ""

Comparing templates in parseJSON confuses me, I don’t understand what is happening with (H.toList -> [(key, value)]).

The data type I want to create looks like this:

data Foo = Bar String
         | Baz String
         | Bin String

I had to do something that I knew how to implement

data Foo = (Maybe Bar) (Maybe Baz) (Maybe Bin)

But that seems unsatisfactory. Can someone help me explain what is happening with the instance Either, and maybe give me some tips on To / From instances for Foo?

update: , , Aeson Maybe, , . , , Either.

+3
3

(Object (H.toList -> [(key, value)])) . - :

parseJSon (Object o) = case H.toList o of
    [(key, value)]
        | key == left  -> Left  <$> parseJSON value
        | key == right -> Right <$> parseJSON value

, Object o Object, , " Object o" "H.toList o [(key, value)] pattern", .

+4

json , , , . Data, . , .

+2

Assuming each data type has a separate key, a different approach might use lenses - I like it because it is concise and readable. For example, if you have a wrapper around A, B, and C that have FromJSON instances:

import Data.Aeson
import Data.Maybe
import Data.Aeson.Lens
import Control.Lens

data Wrap = WrapA A | WrapB B | WrapC C

instance FromJSON Wrap where
    parseJSON json
        | isJust (json ^? key "A unique key") = WrapA <$> parseJSON json
        | isJust (json ^? key "B unique key") = WrapB <$> parseJSON json
        | isJust (json ^? key "C unique key") = WrapC <$> parseJSON json
        | otherwise = fail "Bad message"
+1
source

All Articles