packages feed

aeson-pretty 0.6.3 → 0.7

raw patch · 4 files changed

+184/−103 lines, 4 filesdep +aeson-prettydep −containersdep ~aesondep ~attoparsecdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: aeson-pretty

Dependencies removed: containers

Dependency ranges changed: aeson, attoparsec, base, bytestring, text, vector

API changes (from Hackage documentation)

+ Data.Aeson.Encode.Pretty: Config :: Int -> (Text -> Text -> Ordering) -> Config
+ Data.Aeson.Encode.Pretty: compare :: Ord a => a -> a -> Ordering
+ Data.Aeson.Encode.Pretty: confCompare :: Config -> Text -> Text -> Ordering
+ Data.Aeson.Encode.Pretty: confIndent :: Config -> Int
+ Data.Aeson.Encode.Pretty: data Config
+ Data.Aeson.Encode.Pretty: defConfig :: Config
+ Data.Aeson.Encode.Pretty: keyOrder :: [Text] -> Text -> Text -> Ordering
+ Data.Aeson.Encode.Pretty: mempty :: Monoid a => a
- Data.Aeson.Encode.Pretty: encodePretty' :: ToJSON a => Int -> a -> ByteString
+ Data.Aeson.Encode.Pretty: encodePretty' :: ToJSON a => Config -> a -> ByteString

Files

Data/Aeson/Encode/Pretty.hs view
@@ -1,65 +1,145 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, RecordWildCards #-}  -- |Aeson-compatible pretty-printing of JSON 'Value's.-module Data.Aeson.Encode.Pretty (encodePretty, encodePretty') where+module Data.Aeson.Encode.Pretty (+    -- * Simple Pretty-Printing+    encodePretty,+    +    -- * Pretty-Printing with Configuration Options+    encodePretty',+    Config (..), defConfig,+    -- ** Sorting Keys in Objects+    -- |With the Aeson library, the order of keys in objects is undefined due+    --  objects being implemented as HashMaps. To allow user-specified key+    --  orders in the pretty-printed JSON, 'encodePretty'' can be configured+    --  with a comparison function. These comparison functions can be composed+    --  using the 'Monoid' interface. Some other useful helper functions to keep+    --  in mind are 'comparing' and 'on'.+    --  +    --  Consider the following deliberately convoluted example, demonstrating+    --  the use of comparison functions:+    --+    --  An  object might pretty-print as follows+    --+    --  > {+    --  >   "baz": ...,+    --  >   "bar": ...,+    --  >   "foo": ...,+    --  >   "quux": ...,+    --  > }+    --+    --  which is clearly a confusing order of keys. By using a comparison+    --  function such as+    --+    --  > comp :: Text -> Text -> Ordering+    --  > comp = keyOrder ["foo","bar"] `mappend` comparing length+    --+    --  we can achieve the desired neat result:+    --+    --  > {+    --  >   "foo": ...,+    --  >   "bar": ...,+    --  >   "baz": ...,+    --  >   "quux": ...,+    --  > }+    --+    +    mempty,+    -- |Serves as an order-preserving (non-)sort function. Re-exported from+    --  "Data.Monoid".+    compare,+    -- |Sort keys in their natural order, i.e. by comparing character codes.+    -- Re-exported from the Prelude and "Data.Ord"+    keyOrder+) where  import Data.Aeson (Value(..), ToJSON(..)) import qualified Data.Aeson.Encode as Aeson import Data.ByteString.Lazy (ByteString)+import Data.Function (on) import qualified Data.HashMap.Strict as H (toList)-import Data.List (intersperse)+import Data.List (intersperse, sortBy, elemIndex)+import Data.Maybe (fromMaybe) import Data.Monoid (mappend, mconcat, mempty)+import Data.Ord import Data.Text (Text) import Data.Text.Lazy.Builder (Builder, toLazyText) import Data.Text.Lazy.Encoding (encodeUtf8) import qualified Data.Vector as V (toList) +data PState = PState { pstIndent :: Int+                     , pstLevel  :: Int+                     , pstSort   :: [(Text, Value)] -> [(Text, Value)]+                     } -type Indent = (Int, Int) -- (spaces per lvl, lvl)+data Config = Config+    { confIndent  :: Int+      -- ^ Indentation spaces per level of nesting+    , confCompare :: Text -> Text -> Ordering+      -- ^ Function used to sort keys in objects+    } +-- |Sort keys by their order of appearance in the argument list.+--+--  Keys that are not present in the argument list are considered to be greater+--  than any key in the list and equal to all keys not in the list. I.e. keys+--  not in the argument list are moved to the end, while their order is+--  preserved.+keyOrder :: [Text] -> Text -> Text -> Ordering+keyOrder ks = comparing $ \k -> fromMaybe maxBound (elemIndex k ks)+++-- |The default configuration: indent by four spaces per level of nesting, do+--  not sort objects by key.+--+--  > defConfig = Config { confIndent = 4, confSort = mempty }+defConfig :: Config+defConfig = Config { confIndent = 4, confCompare = mempty }+ -- |A drop-in replacement for aeson's 'Aeson.encode' function, producing  --  JSON-ByteStrings for human readers. -----  Indents by four spaces per nesting-level.+--  Follows the default configuration in 'defConfig'. encodePretty :: ToJSON a => a -> ByteString-encodePretty = encodePretty' 4 -- default indentation is four spaces+encodePretty = encodePretty' defConfig --- |A variant of 'encodePretty' that takes an additional parameter: the number---  of spaces to indent per nesting-level.-encodePretty' :: ToJSON a => Int -> a -> ByteString-encodePretty' spacesPerLvl = encodeUtf8 . toLazyText . fromValue ind . toJSON+-- |A variant of 'encodePretty' that takes an additional configuration+--  parameter.+encodePretty' :: ToJSON a => Config -> a -> ByteString+encodePretty' Config{..} = encodeUtf8 . toLazyText . fromValue st . toJSON   where-    ind = (spacesPerLvl, 0)+    st       = PState confIndent 0 condSort+    condSort = sortBy (confCompare `on` fst) -fromValue :: Indent -> Value -> Builder-fromValue ind = go+fromValue :: PState -> Value -> Builder+fromValue st@PState{..} = go   where-    go (Array v)  = fromCompound ind ("[","]") fromValue (V.toList v)-    go (Object m) = fromCompound ind ("{","}") fromPair (H.toList m)+    go (Array v)  = fromCompound st ("[","]") fromValue (V.toList v)+    go (Object m) = fromCompound st ("{","}") fromPair (pstSort (H.toList m))     go v          = Aeson.fromValue v -fromCompound :: Indent+fromCompound :: PState              -> (Builder, Builder)-             -> (Indent -> a -> Builder)+             -> (PState -> a -> Builder)              -> [a]              -> Builder-fromCompound ind (delimL,delimR) fromItem items = mconcat+fromCompound st@PState{..} (delimL,delimR) fromItem items = mconcat     [ delimL     , if null items then mempty-        else "\n" <> items' <> "\n" <> fromIndent ind+        else "\n" <> items' <> "\n" <> fromIndent st     , delimR     ]   where     items' = mconcat . intersperse ",\n" $-                map (\item -> fromIndent ind' <> fromItem ind' item)+                map (\item -> fromIndent st' <> fromItem st' item)                     items-    ind' = let (spacesPerLvl, lvl) = ind in (spacesPerLvl, lvl + 1)+    st' = st { pstLevel = pstLevel + 1 } -fromPair :: Indent -> (Text, Value) -> Builder-fromPair ind (k,v) = Aeson.fromValue (toJSON k) <> ": " <> fromValue ind v+fromPair :: PState -> (Text, Value) -> Builder+fromPair st (k,v) = Aeson.fromValue (toJSON k) <> ": " <> fromValue st v -fromIndent :: Indent -> Builder-fromIndent (spacesPerLvl, lvl) = mconcat $ replicate (spacesPerLvl * lvl) " "+fromIndent :: PState -> Builder+fromIndent PState{..} = mconcat $ replicate (pstIndent * pstLevel) " "  (<>) :: Builder -> Builder -> Builder (<>) = mappend
− Main.hs
@@ -1,53 +0,0 @@-{-# LANGUAGE DeriveDataTypeable, RecordWildCards, OverloadedStrings #-}-module Main (main) where--import Prelude hiding (interact, concat, unlines, null)-import Data.Aeson (Value(..), json', encode)-import Data.Aeson.Encode.Pretty-import Data.Attoparsec.Lazy (Result(..), parse)-import Data.ByteString.Lazy.Char8 (ByteString, interact, unlines, null)-import Data.Version (showVersion)-import Paths_aeson_pretty (version)-import System.Console.CmdArgs---data Options = Opts { compact :: Bool-                    , indent  :: Int-                    }-    deriving (Data, Typeable)--opts :: Options-opts = Opts-    { compact = False &= help "Compact output."-    , indent  = 4     &= help "Number of spaces per nesting-level (default 4)."-    }   &= program prog-        &= summary smry-        &= details info-  where-    prog = "aeson-pretty"-    smry = prog++" "++showVersion version++": Pretty JSON, the easy way."--info :: [String]-info =-    [ "Read JSON from stdin and pretty-print to stdout. The complementary "-    , "compact-mode removes whitespace from the input."-    , ""-    , "(c) Falko Peters 2011"-    , ""-    , "License: BSD3, for details see the source-repository at"-    , "http://www.github.com/informatikr/aeson-pretty."-    , ""-    ]--main :: IO ()-main = do-    Opts{..} <- cmdArgs opts-    let enc = if compact then encode else encodePretty' indent-    interact $ unlines . map enc . values--values :: ByteString -> [Value]-values s = case parse json' s of-            Done rest v     -> v : values rest-            Fail rest _ _-                | null rest -> []-                | otherwise -> error "invalid json"
aeson-pretty.cabal view
@@ -1,5 +1,5 @@ name:           aeson-pretty-version:        0.6.3+version:        0.7 license:        BSD3 license-file:   LICENSE category:       Text, Web, JSON, Pretty Printer@@ -35,41 +35,37 @@     description: Only build/install the library, NOT the command-line tool.     default: False +library+    exposed-modules:+        Data.Aeson.Encode.Pretty++    build-depends:+        aeson >= 0.6,+        base == 4.*,+        bytestring >= 0.9,+        vector >= 0.9,+        text >= 0.11,+        unordered-containers >= 0.1.3.0++    ghc-options: -Wall+ executable aeson-pretty+    hs-source-dirs: cli-tool     main-is: Main.hs      if flag(lib-only)         buildable: False     else         build-depends:-            aeson == 0.6.*,-            attoparsec == 0.10.*,+            aeson >= 0.6,+            aeson-pretty,+            attoparsec >= 0.10,             base == 4.*,-            bytestring >= 0.9 && < 0.11,-            cmdargs >= 0.7,-            containers,-            vector >= 0.9 && < 0.11,-            text == 0.11.*,-            unordered-containers >= 0.1.3.0+            bytestring >= 0.9,+            cmdargs >= 0.7      ghc-options: -Wall     ghc-prof-options: -auto-all-    -library-    exposed-modules:-        Data.Aeson.Encode.Pretty--    build-depends:-        aeson == 0.6.*,-        attoparsec == 0.10.*,-        base == 4.*,-        bytestring >= 0.9 && < 0.11,-        containers,-        vector >= 0.9 && < 0.11,-        text == 0.11.*,-        unordered-containers >= 0.1.3.0--    ghc-options: -Wall  source-repository head     type:     git
+ cli-tool/Main.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE DeriveDataTypeable, RecordWildCards, OverloadedStrings #-}+module Main (main) where++import Prelude hiding (interact, concat, unlines, null)+import Data.Aeson (Value(..), json', encode)+import Data.Aeson.Encode.Pretty+import Data.Attoparsec.Lazy (Result(..), parse)+import Data.ByteString.Lazy.Char8 (ByteString, interact, unlines, null)+import Data.Version (showVersion)+import Paths_aeson_pretty (version)+import System.Console.CmdArgs+++data Options = Opts { compact :: Bool+                    , indent  :: Int+                    , sort    :: Bool+                    }+    deriving (Data, Typeable)++opts :: Options+opts = Opts+    { compact = False &= help "Compact output."+    , indent  = 4     &= help "Number of spaces per nesting-level (default 4)."+    , sort    = False &= help "Sort objects by key (default false)."+    }   &= program prog+        &= summary smry+        &= details info+  where+    prog = "aeson-pretty"+    smry = prog++" "++showVersion version++": Pretty JSON, the easy way."++info :: [String]+info =+    [ "Read JSON from stdin and pretty-print to stdout. The complementary "+    , "compact-mode removes whitespace from the input."+    , ""+    , "(c) Falko Peters 2011"+    , ""+    , "License: BSD3, for details see the source-repository at"+    , "http://www.github.com/informatikr/aeson-pretty."+    , ""+    ]++main :: IO ()+main = do+    Opts{..} <- cmdArgs opts+    let conf = Config { confIndent  = indent+                      , confCompare = if sort then compare else mempty+                      }+        enc = if compact then encode else encodePretty' conf+    interact $ unlines . map enc . values++values :: ByteString -> [Value]+values s = case parse json' s of+            Done rest v     -> v : values rest+            Fail rest _ _+                | null rest -> []+                | otherwise -> error "invalid json"