Skip to content

Commit

Permalink
imp: json: The keys of JSON objects are now displayed in alphabetical…
Browse files Browse the repository at this point in the history
… order, making it stable across different systems and compilers.
  • Loading branch information
Xitian9 committed Jul 21, 2021
1 parent 8653510 commit 071ecd4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 41 deletions.
14 changes: 8 additions & 6 deletions hledger-lib/Hledger/Data/Json.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ module Hledger.Data.Json (
) where

import Data.Aeson
import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder)
import Data.Aeson.Encode.Pretty (Config(..), Indent(..), NumberFormat(..),
encodePretty', encodePrettyToTextBuilder')
--import Data.Aeson.TH
import qualified Data.ByteString.Lazy as BL
import Data.Decimal (DecimalRaw(..), roundTo)
import Data.Maybe (fromMaybe)
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TL
import qualified Data.Text.Lazy.Builder as TB
import GHC.Generics (Generic)
import System.Time (ClockTime)
Expand Down Expand Up @@ -259,15 +259,18 @@ instance FromJSON (DecimalRaw Integer)

-- Utilities

-- | Config for pretty printing JSON output.
jsonConf :: Config
jsonConf = Config{confIndent=Spaces 4,confCompare=compare, confNumFormat=Generic, confTrailingNewline=True}

-- | Show a JSON-convertible haskell value as pretty-printed JSON text.
toJsonText :: ToJSON a => a -> TL.Text
toJsonText = TB.toLazyText . (<> TB.fromText "\n") . encodePrettyToTextBuilder
toJsonText = TB.toLazyText . encodePrettyToTextBuilder' jsonConf

-- | Write a JSON-convertible haskell value to a pretty-printed JSON file.
-- Eg: writeJsonFile "a.json" nulltransaction
writeJsonFile :: ToJSON a => FilePath -> a -> IO ()
writeJsonFile f = TL.writeFile f . toJsonText
-- we write with Text and read with ByteString, is that fine ?
writeJsonFile f = BL.writeFile f . encodePretty' jsonConf

-- | Read a JSON file and decode it to the target type, or raise an error if we can't.
-- Eg: readJsonFile "a.json" :: IO Transaction
Expand All @@ -280,4 +283,3 @@ readJsonFile f = do
case fromJSON v :: FromJSON a => Result a of
Error e -> error e
Success t -> return t

70 changes: 35 additions & 35 deletions hledger/test/json.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,52 @@ $ hledger -f- reg --output-format=json
null,
"",
{
"pbalanceassertion": null,
"pstatus": "Unmarked",
"paccount": "a",
"pamount": [
{
"aprice": null,
"acommodity": "AAA",
"aismultiplier": false,
"aprice": null,
"aquantity": {
"floatingPoint": 1,
"decimalMantissa": 10,
"decimalPlaces": 1,
"decimalMantissa": 10
"floatingPoint": 1
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "R",
"asdigitgroups": null,
"ascommodityspaced": true,
"asprecision": 1,
"asdecimalpoint": "."
"asdecimalpoint": ".",
"asdigitgroups": null,
"asprecision": 1
}
}
],
"ptransaction_": "1",
"paccount": "a",
"pdate": null,
"ptype": "VirtualPosting",
"pbalanceassertion": null,
"pcomment": "",
"pdate": null,
"pdate2": null,
"poriginal": null,
"pstatus": "Unmarked",
"ptags": [],
"poriginal": null
"ptransaction_": "1",
"ptype": "VirtualPosting"
},
[
{
"aprice": null,
"acommodity": "AAA",
"aismultiplier": false,
"aprice": null,
"aquantity": {
"floatingPoint": 1,
"decimalMantissa": 10,
"decimalPlaces": 1,
"decimalMantissa": 10
"floatingPoint": 1
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "R",
"asdigitgroups": null,
"ascommodityspaced": true,
"asprecision": 1,
"asdecimalpoint": "."
"asdecimalpoint": ".",
"asdigitgroups": null,
"asprecision": 1
}
}
]
Expand All @@ -72,41 +72,41 @@ $ hledger -f- bal --output-format=json
0,
[
{
"aprice": null,
"acommodity": "AAA",
"aismultiplier": false,
"aprice": null,
"aquantity": {
"floatingPoint": 1,
"decimalMantissa": 10,
"decimalPlaces": 1,
"decimalMantissa": 10
"floatingPoint": 1
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "R",
"asdigitgroups": null,
"ascommodityspaced": true,
"asprecision": 1,
"asdecimalpoint": "."
"asdecimalpoint": ".",
"asdigitgroups": null,
"asprecision": 1
}
}
]
]
],
[
{
"aprice": null,
"acommodity": "AAA",
"aismultiplier": false,
"aprice": null,
"aquantity": {
"floatingPoint": 1,
"decimalMantissa": 10,
"decimalPlaces": 1,
"decimalMantissa": 10
"floatingPoint": 1
},
"aismultiplier": false,
"astyle": {
"ascommodityside": "R",
"asdigitgroups": null,
"ascommodityspaced": true,
"asprecision": 1,
"asdecimalpoint": "."
"asdecimalpoint": ".",
"asdigitgroups": null,
"asprecision": 1
}
}
]
Expand Down

2 comments on commit 071ecd4

@simonmichael
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terrific. Shall we reduce the indent to 2 ? It would save some spaces and show more data in narrow windows.
I suppose there's no real way to keep the order of fields as they were defined in Types.hs, right ?

@Xitian9
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, sort of. If we use toEncoding this will maintain the order, but will not have any pretty-printing. If we want pretty-printing we would have to manually define every comparison, which seems like more effort than it's worth.

Please sign in to comment.