-- | -- There are two ways to use this library: the way with extra boilerplate, -- or the way with a leaky abstraction. A truly good solution would fix it -- all with Template Haskell; but the author is not a truly good Haskeller, -- so this will have to do for now. -- -- The boilerplate way: -- -- @ -- data Hideous = Hideous { yak :: [String] } -- instance ToJSON Hideous where -- toJSON x = object [ "yak" .= hairy (yak x) ] -- instance FromJSON Hideous where -- parseJSON (Object o) = Hideous \<$> (shave \<$> o .:? "yak") -- @ -- >>> encode $ Hideous ["foo","bar"] -- "{\"yak\":[\"foo\",\"bar\"]}" -- >>> encode $ Hideous ["baz"] -- "{\"yak\":\"baz\"}" -- >>> encode $ Hideous [] -- "{\"yak\":null}" -- >>> yak <$> decode "{\"yak\":[\"foo\",\"bar\"]}" -- Just ["foo", "bar"] -- >>> yak <$> decode "{\"yak\":\"baz\"}" -- Just ["baz"] -- >>> yak <$> decode "{}" -- Just [] -- -- The leaky way: -- -- @ -- data Abhorrent = Abhorrent { yak :: Yak String } -- $(deriveJSON defaultOptions{omitNothingFields=True} ''Abhorrent) -- @ -- >>> encode . Abhorrent . hairy $ ["shaven","shorn","sheared"] -- "{\"yak\":[\"shaven\",\"shorn\",\"sheared\"]}" -- >>> shave . yak <$> decode "{}" -- Just [] -- -- Which to prefer depends on how many yaks you need to deal with /vs./ how -- much you hate cleaning up yak droppings in the rest of your codebase. module Data.Aeson.Yak ( Yak , hairy , shave) where import Data.Aeson import Data.Foldable -- | Data whose JSON representation may legally be an array, a single element, -- or null\/absent. No, please, calm down. It'll be okay. Mostly. -- -- /('Lousy' is not exposed to avoid namespace infestation./ -- /This is open for discussion if a use case can be shown.)/ type Yak a = Maybe (Lousy a) -- | Convert a @'Foldable'@ to a 'Yak'. Should probably be specific to lists, -- but what's life without a little adventure? hairy :: (Foldable f) => f a -> Yak a hairy beast = case toList beast of [] -> Nothing yak -> Just (Lousy yak) -- | Convert a 'Yak' to a list. Relax, and allow yourself to breathe. shave :: Yak a -> [a] shave Nothing = [] shave (Just nit) = pick nit newtype Lousy a = Lousy { pick :: [a] } deriving (Eq) instance (Show a) => Show (Lousy a) where show = ("Lousy " ++) . show . pick instance (ToJSON a) => ToJSON (Lousy a) where toJSON nit = case pick nit of [x] -> toJSON x xs -> toJSON xs instance (FromJSON a) => FromJSON (Lousy a) where parseJSON vs@(Array _) = Lousy <$> parseJSON vs parseJSON v = (\a -> Lousy [a]) <$> parseJSON v