aeson-yak (empty) → 0.1.0.0
raw patch · 4 files changed
+141/−0 lines, 4 filesdep +aesondep +basesetup-changed
Dependencies added: aeson, base
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- aeson-yak.cabal +40/−0
- src/Data/Aeson/Yak.hs +79/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Theodore Lief Gannon + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple +main = defaultMain
+ aeson-yak.cabal view
@@ -0,0 +1,40 @@+-- Initial aeson-yak.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: aeson-yak +version: 0.1.0.0 +synopsis: Handle JSON that may or may not be a list, or exist +description: + According to the standard promoted by Schema.org, the following are all + legally the same object: + + \{ field: [data1, data2] } + \{ field: data } + \{ } + + This library provides an intermediary data type and supporting functions to + help aeson parse and output JSON conforming to this frustrating standard. + +homepage: https://github.com/tejon/aeson-yak +license: MIT +license-file: LICENSE +author: Theodore Lief Gannon +maintainer: tanuki@gmail.com +copyright: Copyright (c) Theodore Lief Gannon, 2015 +category: Data +build-type: Simple +-- extra-source-files: +cabal-version: >=1.10 + +source-repository head + type: git + location: https://github.com/tejon/aeson-yak + +library + exposed-modules: Data.Aeson.Yak + -- other-modules: + -- other-extensions: + build-depends: base >=4.8 && <4.9 + , aeson >= 0.4 && < 0.11 + hs-source-dirs: src + default-language: Haskell2010
+ src/Data/Aeson/Yak.hs view
@@ -0,0 +1,79 @@+-- | +-- 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] } + +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