aeson-yak-0.1.1.3: src/Data/Aeson/Yak.hs
{-|
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