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 +105/−25
- Main.hs +0/−53
- aeson-pretty.cabal +21/−25
- cli-tool/Main.hs +58/−0
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"