aeson-pretty 0.7.2 → 0.8.11
raw patch · 5 files changed
Files
- CHANGELOG.markdown +12/−0
- Data/Aeson/Encode/Pretty.hs +96/−28
- README.markdown +4/−1
- aeson-pretty.cabal +20/−14
- cli-tool/Main.hs +16/−5
+ CHANGELOG.markdown view
@@ -0,0 +1,12 @@+# aeson-pretty changelog++## 0.8.11+ * Dropped support for Aeson 1.4 and older+ * Added support for Aeson 2.3+ * Added support for Aeson 2.2++## 0.8.10+ * Added support for Aeson 2.1++## 0.8.9+ * Added support for Aeson 2.0
Data/Aeson/Encode/Pretty.hs view
@@ -1,21 +1,22 @@-{-# LANGUAGE OverloadedStrings, RecordWildCards #-}+{-# LANGUAGE OverloadedStrings, RecordWildCards, CPP #-} -- |Aeson-compatible pretty-printing of JSON 'Value's. module Data.Aeson.Encode.Pretty ( -- * Simple Pretty-Printing encodePretty, encodePrettyToTextBuilder,- + -- * Pretty-Printing with Configuration Options encodePretty', encodePrettyToTextBuilder', Config (..), defConfig,+ Indent(..), NumberFormat(..), -- ** Sorting Keys in Objects- -- |With the Aeson library, the order of keys in objects is undefined due+ -- |With the Aeson library, the order of keys in objects is undefined due to -- 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: --@@ -43,7 +44,7 @@ -- > "quux": ..., -- > } --- + mempty, -- |Serves as an order-preserving (non-)sort function. Re-exported from -- "Data.Monoid".@@ -53,30 +54,68 @@ keyOrder ) where +#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.Key as AK+import qualified Data.Aeson.KeyMap as AKM+#endif import Data.Aeson (Value(..), ToJSON(..))-import qualified Data.Aeson.Encode as Aeson+import qualified Data.Aeson.Text as Aeson import Data.ByteString.Lazy (ByteString) import Data.Function (on)+#if !MIN_VERSION_aeson(2,0,0) import qualified Data.HashMap.Strict as H (toList)+#endif import Data.List (intersperse, sortBy, elemIndex) import Data.Maybe (fromMaybe)-import Data.Monoid ((<>), mconcat, mempty)-import Data.Ord+#if !MIN_VERSION_base(4,13,0)+import Data.Semigroup ((<>))+#endif+import qualified Data.Scientific as S (Scientific, FPFormat(..))+import Data.Ord (comparing) import Data.Text (Text) import Data.Text.Lazy.Builder (Builder, toLazyText)+import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder) import Data.Text.Lazy.Encoding (encodeUtf8) import qualified Data.Vector as V (toList)+import Prelude ()+import Prelude.Compat -data PState = PState { pstIndent :: Int- , pstLevel :: Int- , pstSort :: [(Text, Value)] -> [(Text, Value)]++data PState = PState { pLevel :: Int+ , pIndent :: Builder+ , pNewline :: Builder+ , pItemSep :: Builder+ , pKeyValSep :: Builder+ , pNumFormat :: NumberFormat+ , pSort :: [(Text, Value)] -> [(Text, Value)] } +-- | Indentation per level of nesting. @'Spaces' 0@ removes __all__ whitespace+-- from the output.+data Indent = Spaces Int | Tab++data NumberFormat+ -- | For numbers with absolute value less than 1e19, this follows the standard+ -- behaviour of the 'Data.Aeson.encode' function. Uses integer literals for+ -- integers (1, 2, 3...), simple decimals for fractional values between 0.1+ -- and 9,999,999, and scientific notation otherwise. For numbers with an+ -- absolute value larger than 1e19, always uses scientific notation.+ = Generic+ -- | Scientific notation (e.g. 2.3e123).+ | Scientific+ -- | Standard decimal notation+ | Decimal+ -- | Custom formatting function+ | Custom (S.Scientific -> Builder)+ data Config = Config- { confIndent :: Int- -- ^ Indentation spaces per level of nesting+ { confIndent :: Indent+ -- ^ Indentation per level of nesting , confCompare :: Text -> Text -> Ordering -- ^ Function used to sort keys in objects+ , confNumFormat :: NumberFormat+ , confTrailingNewline :: Bool+ -- ^ Whether to add a trailing newline to the output } -- |Sort keys by their order of appearance in the argument list.@@ -90,13 +129,14 @@ -- |The default configuration: indent by four spaces per level of nesting, do--- not sort objects by key.+-- not sort objects by key, do not add trailing newline. ----- > defConfig = Config { confIndent = 4, confCompare = mempty }+-- > defConfig = Config { confIndent = Spaces 4, confCompare = mempty, confNumFormat = Generic, confTrailingNewline = False } defConfig :: Config-defConfig = Config { confIndent = 4, confCompare = mempty }+defConfig =+ Config {confIndent = Spaces 4, confCompare = mempty, confNumFormat = Generic, confTrailingNewline = False} --- |A drop-in replacement for aeson's 'Aeson.encode' function, producing +-- |A drop-in replacement for aeson's 'Aeson.encode' function, producing -- JSON-ByteStrings for human readers. -- -- Follows the default configuration in 'defConfig'.@@ -115,22 +155,40 @@ encodePrettyToTextBuilder :: ToJSON a => a -> Builder encodePrettyToTextBuilder = encodePrettyToTextBuilder' defConfig --- |A variant of 'encodeToTextBuilder' that takes an additional configuration+-- |A variant of 'Aeson.encodeToTextBuilder' that takes an additional configuration -- parameter. encodePrettyToTextBuilder' :: ToJSON a => Config -> a -> Builder-encodePrettyToTextBuilder' Config{..} = fromValue st . toJSON+encodePrettyToTextBuilder' Config{..} x = fromValue st (toJSON x) <> trail where- st = PState confIndent 0 condSort- condSort = sortBy (confCompare `on` fst)+ st = PState 0 indent newline itemSep kvSep confNumFormat sortFn+ indent = case confIndent of+ Spaces n -> mconcat (replicate n " ")+ Tab -> "\t"+ newline = case confIndent of+ Spaces 0 -> ""+ _ -> "\n"+ itemSep = ","+ kvSep = case confIndent of+ Spaces 0 -> ":"+ _ -> ": "+ sortFn = sortBy (confCompare `on` fst)+ trail = if confTrailingNewline then "\n" else "" fromValue :: PState -> Value -> Builder-fromValue st@PState{..} = go+fromValue st@PState{..} val = go val where go (Array v) = fromCompound st ("[","]") fromValue (V.toList v)- go (Object m) = fromCompound st ("{","}") fromPair (pstSort (H.toList m))+ go (Object m) = fromCompound st ("{","}") fromPair (pSort (toList' m))+ go (Number x) = fromNumber st x go v = Aeson.encodeToTextBuilder v +#if MIN_VERSION_aeson(2,0,0)+ toList' = fmap (\(k, v) -> (AK.toText k, v)) . AKM.toList+#else+ toList' = H.toList+#endif+ fromCompound :: PState -> (Builder, Builder) -> (PState -> a -> Builder)@@ -139,17 +197,27 @@ fromCompound st@PState{..} (delimL,delimR) fromItem items = mconcat [ delimL , if null items then mempty- else "\n" <> items' <> "\n" <> fromIndent st+ else pNewline <> items' <> pNewline <> fromIndent st , delimR ] where- items' = mconcat . intersperse ",\n" $+ items' = mconcat . intersperse (pItemSep <> pNewline) $ map (\item -> fromIndent st' <> fromItem st' item) items- st' = st { pstLevel = pstLevel + 1 }+ st' = st { pLevel = pLevel + 1} fromPair :: PState -> (Text, Value) -> Builder-fromPair st (k,v) = Aeson.encodeToTextBuilder (toJSON k) <> ": " <> fromValue st v+fromPair st (k,v) =+ Aeson.encodeToTextBuilder (toJSON k) <> pKeyValSep st <> fromValue st v fromIndent :: PState -> Builder-fromIndent PState{..} = mconcat $ replicate (pstIndent * pstLevel) " "+fromIndent PState{..} = mconcat (replicate pLevel pIndent)++fromNumber :: PState -> S.Scientific -> Builder+fromNumber st x = case pNumFormat st of+ Generic+ | (x > 1.0e19 || x < -1.0e19) -> formatScientificBuilder S.Exponent Nothing x+ | otherwise -> Aeson.encodeToTextBuilder $ Number x+ Scientific -> formatScientificBuilder S.Exponent Nothing x+ Decimal -> formatScientificBuilder S.Fixed Nothing x+ Custom f -> f x
README.markdown view
@@ -18,6 +18,9 @@ * `git clone git://github.com/informatikr/aeson-pretty.git` +# Aeson / GHC support+We support all GHCs supported by the latest Aeson release. This in turn determines which Aeson releases we support.+ # Authors -This library is written and maintained by Falko Peters, <falko.peters@gmail.com>.+This library is written by Falko Peters <falko.peters@gmail.com> and maintained by Martijn Bastiaan <martijn@qbaylogic.com>.
aeson-pretty.cabal view
@@ -1,20 +1,20 @@+cabal-version: 2.0 name: aeson-pretty-version: 0.7.2+version: 0.8.11 license: BSD3 license-file: LICENSE category: Text, Web, JSON, Pretty Printer copyright: Copyright 2011 Falko Peters author: Falko Peters <falko.peters@gmail.com>-maintainer: Falko Peters <falko.peters@gmail.com>+maintainer: Martijn Bastiaan <martijn@hmbastiaan.nl> stability: experimental-cabal-version: >= 1.8 homepage: http://github.com/informatikr/aeson-pretty bug-reports: http://github.com/informatikr/aeson-pretty/issues build-type: Simple synopsis: JSON pretty-printing library and command-line tool. description: A JSON pretty-printing library compatible with aeson as well as- a command-line tool to improve readabilty of streams of JSON data.+ a command-line tool to improve readability of streams of JSON data. . The /library/ provides the function "encodePretty". It is a drop-in replacement for aeson's "encode" function, producing JSON-ByteStrings for@@ -22,36 +22,41 @@ . The /command-line tool/ reads JSON from stdin and writes prettified JSON to stdout. It also offers a complementary "compact"-mode, essentially the- opposite of pretty-printing. If you specify @-flib-only@ like this- .- > cabal install -flib-only aeson-pretty- .- the command-line tool will NOT be installed.+ opposite of pretty-printing. extra-source-files: README.markdown +extra-doc-files:+ CHANGELOG.markdown+ flag lib-only description: Only build/install the library, NOT the command-line tool. default: False+ manual: True library exposed-modules: Data.Aeson.Encode.Pretty build-depends:- aeson >= 0.7,- base >= 4.5,+ aeson >=1.5 && <2.4,+ base >= 4.10 && < 4.23,+ base-compat >= 0.9, bytestring >= 0.9,+ scientific >= 0.3, vector >= 0.9, text >= 0.11,- unordered-containers >= 0.1.3.0+ unordered-containers >= 0.2.14.0 ghc-options: -Wall+ default-language: Haskell2010 executable aeson-pretty hs-source-dirs: cli-tool main-is: Main.hs+ other-modules: Paths_aeson_pretty+ autogen-modules: Paths_aeson_pretty if flag(lib-only) buildable: False@@ -60,12 +65,13 @@ aeson >= 0.6, aeson-pretty, attoparsec >= 0.10,- base == 4.*,+ attoparsec-aeson,+ base >= 4.10 && < 4.23, bytestring >= 0.9, cmdargs >= 0.7 ghc-options: -Wall- ghc-prof-options: -auto-all+ default-language: Haskell2010 source-repository head type: git
cli-tool/Main.hs view
@@ -1,9 +1,14 @@-{-# LANGUAGE DeriveDataTypeable, RecordWildCards, OverloadedStrings #-}+{-# LANGUAGE CPP, DeriveDataTypeable, RecordWildCards, OverloadedStrings, PackageImports #-} module Main (main) where import Prelude hiding (interact, concat, unlines, null)-import Data.Aeson (Value(..), json', encode)+import Data.Aeson (Value(..), encode) import Data.Aeson.Encode.Pretty+#if MIN_VERSION_aeson(2,2,0)+import "attoparsec-aeson" Data.Aeson.Parser.Internal (value')+#else+import "aeson" Data.Aeson.Parser.Internal (value')+#endif import Data.Attoparsec.Lazy (Result(..), parse) import Data.ByteString.Lazy.Char8 (ByteString, interact, unlines, null) import Data.Version (showVersion)@@ -15,7 +20,11 @@ , indent :: Int , sort :: Bool }+#if __GLASGOW_HASKELL__ >= 912+ deriving (Data)+#else deriving (Data, Typeable)+#endif opts :: Options opts = Opts@@ -44,14 +53,16 @@ main :: IO () main = do Opts{..} <- cmdArgs opts- let conf = Config { confIndent = indent- , confCompare = if sort then compare else mempty+ let conf = Config { confIndent = Spaces indent+ , confCompare = if sort then compare else mempty+ , confNumFormat = Generic+ , confTrailingNewline = False } enc = if compact then encode else encodePretty' conf interact $ unlines . map enc . values values :: ByteString -> [Value]-values s = case parse json' s of+values s = case parse value' s of Done rest v -> v : values rest Fail rest _ _ | null rest -> []