    {-
    JSON instances. Should they be in Types.hs ?
    -}

    {-# LANGUAGE CPP               #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE LambdaCase        #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE RecordWildCards   #-}

    module Hledger.Data.Json (
      -- * Instances
      -- * Utilities
       toJsonText
      ,writeJsonFile
      ,readJsonFile
    ) where

    import           Data.Aeson
    import           Data.Aeson.Encode.Pretty (Config(..), Indent(..), NumberFormat(..),
                         encodePretty', encodePrettyToTextBuilder')
    --import           Data.Aeson.TH
    import Data.ByteString.Lazy qualified as BL
    import           Data.Decimal (DecimalRaw(..), roundTo)
    import           Data.Maybe (fromMaybe)
    import Data.Text.Lazy qualified    as TL
    import Data.Text.Lazy.Builder qualified as TB
    import Data.Map qualified as M
    import           Text.Megaparsec (Pos, SourcePos, mkPos, unPos)

    import           Hledger.Data.Types
    import           Hledger.Utils.IO (error')
    import           Hledger.Data.Amount (amountsRaw, mixed)

    -- To JSON

    instance ToJSON Status
    instance ToJSON SourcePos

    -- Use the same encoding as the underlying Int
    instance ToJSON Pos where
      toJSON :: Pos -> Value
    toJSON = Int -> Value
    forall a. ToJSON a => a -> Value
    toJSON (Int -> Value) -> (Pos -> Int) -> Pos -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Pos -> Int
    unPos
      toEncoding :: Pos -> Encoding
    toEncoding = Int -> Encoding
    forall a. ToJSON a => a -> Encoding
    toEncoding (Int -> Encoding) -> (Pos -> Int) -> Pos -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Pos -> Int
    unPos

    -- https://github.com/simonmichael/hledger/issues/1195

    -- The default JSON output for Decimal can contain 255-digit integers
    -- (for repeating decimals caused by implicit transaction prices).
    -- JSON output is intended to be consumed by diverse apps and
    -- programming languages, which can't handle numbers like that.
    -- From #1195:
    --
    -- > - JavaScript uses 64-bit IEEE754 numbers which can only accurately
    -- >   represent integers up to 9007199254740991 (i.e. a maximum of 15 digits).
    -- > - Java’s largest integers are limited to 18 digits.
    -- > - Python 3 integers are unbounded.
    -- > - Python 2 integers are limited to 18 digits like Java.
    -- > - C and C++ number limits depend on platform — most platforms should
    -- >   be able to represent unsigned integers up to 64 bits, i.e. 19 digits.
    --
    -- What is the best compromise for both accuracy and practicality ?
    -- For now, we provide both the maximum precision representation
    -- (decimalPlaces & decimalMantissa), and a floating point representation
    -- with up to 10 decimal places (and an unbounded number of integer digits).
    -- We hope the mere presence of the large number in JSON won't break things,
    -- and that the overall number of significant digits in the floating point
    -- remains manageable in practice. (I'm not sure how to limit the number
    -- of significant digits in a Decimal right now.)
    instance (Integral a, ToJSON a) => ToJSON (DecimalRaw a) where
      toJSON :: DecimalRaw a -> Value
    toJSON = [Pair] -> Value
    object ([Pair] -> Value)
    -> (DecimalRaw a -> [Pair]) -> DecimalRaw a -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DecimalRaw a -> [Pair]
    forall e kv a.
    (KeyValue e kv, Integral a, ToJSON a) =>
    DecimalRaw a -> [kv]
    decimalKV
      toEncoding :: DecimalRaw a -> Encoding
    toEncoding = Series -> Encoding
    pairs (Series -> Encoding)
    -> (DecimalRaw a -> Series) -> DecimalRaw a -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Series] -> Series
    forall a. Monoid a => [a] -> a
    mconcat ([Series] -> Series)
    -> (DecimalRaw a -> [Series]) -> DecimalRaw a -> Series
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DecimalRaw a -> [Series]
    forall e kv a.
    (KeyValue e kv, Integral a, ToJSON a) =>
    DecimalRaw a -> [kv]
    decimalKV

    decimalKV :: (
    #if MIN_VERSION_aeson(2,2,0)
      KeyValue e kv,
    #else
      KeyValue kv,
    #endif
      Integral a, ToJSON a) => DecimalRaw a -> [kv]
    decimalKV :: forall e kv a.
    (KeyValue e kv, Integral a, ToJSON a) =>
    DecimalRaw a -> [kv]
    decimalKV DecimalRaw a
    d = let d' :: DecimalRaw a
    d' = if DecimalRaw a -> Word8
    forall i. DecimalRaw i -> Word8
    decimalPlaces DecimalRaw a
    d Word8 -> Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Word8
    10 then DecimalRaw a
    d else Word8 -> DecimalRaw a -> DecimalRaw a
    forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
    roundTo Word8
    10 DecimalRaw a
    d in
        [ Key
    "decimalPlaces"   Key -> Word8 -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= DecimalRaw a -> Word8
    forall i. DecimalRaw i -> Word8
    decimalPlaces DecimalRaw a
    d'
        , Key
    "decimalMantissa" Key -> a -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= DecimalRaw a -> a
    forall i. DecimalRaw i -> i
    decimalMantissa DecimalRaw a
    d'
        , Key
    "floatingPoint"   Key -> Double -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= (DecimalRaw a -> Double
    forall a b. (Real a, Fractional b) => a -> b
    realToFrac DecimalRaw a
    d' :: Double)
        ]

    instance ToJSON CostBasis
    instance ToJSON Amount
    instance ToJSON Rounding
    instance ToJSON AmountStyle

    -- Use the same JSON serialisation as Maybe Word8
    instance ToJSON AmountPrecision where
      toJSON :: AmountPrecision -> Value
    toJSON = Maybe Word8 -> Value
    forall a. ToJSON a => a -> Value
    toJSON (Maybe Word8 -> Value)
    -> (AmountPrecision -> Maybe Word8) -> AmountPrecision -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . \case
        Precision Word8
    n      -> Word8 -> Maybe Word8
    forall a. a -> Maybe a
    Just Word8
    n
        AmountPrecision
    NaturalPrecision -> Maybe Word8
    forall a. Maybe a
    Nothing
      toEncoding :: AmountPrecision -> Encoding
    toEncoding = Maybe Word8 -> Encoding
    forall a. ToJSON a => a -> Encoding
    toEncoding (Maybe Word8 -> Encoding)
    -> (AmountPrecision -> Maybe Word8) -> AmountPrecision -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . \case
        Precision Word8
    n      -> Word8 -> Maybe Word8
    forall a. a -> Maybe a
    Just Word8
    n
        AmountPrecision
    NaturalPrecision -> Maybe Word8
    forall a. Maybe a
    Nothing

    instance ToJSON Side
    instance ToJSON DigitGroupStyle

    instance ToJSON MixedAmount where
      toJSON :: MixedAmount -> Value
    toJSON = [Amount] -> Value
    forall a. ToJSON a => a -> Value
    toJSON ([Amount] -> Value)
    -> (MixedAmount -> [Amount]) -> MixedAmount -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MixedAmount -> [Amount]
    amountsRaw
      toEncoding :: MixedAmount -> Encoding
    toEncoding = [Amount] -> Encoding
    forall a. ToJSON a => a -> Encoding
    toEncoding ([Amount] -> Encoding)
    -> (MixedAmount -> [Amount]) -> MixedAmount -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MixedAmount -> [Amount]
    amountsRaw

    instance ToJSON BalanceAssertion
    instance ToJSON AmountCost
    instance ToJSON MarketPrice
    instance ToJSON PostingType

    instance ToJSON Posting where
      toJSON :: Posting -> Value
    toJSON = [Pair] -> Value
    object ([Pair] -> Value) -> (Posting -> [Pair]) -> Posting -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Posting -> [Pair]
    forall e kv. KeyValue e kv => Posting -> [kv]
    postingKV
      toEncoding :: Posting -> Encoding
    toEncoding = Series -> Encoding
    pairs (Series -> Encoding) -> (Posting -> Series) -> Posting -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Series] -> Series
    forall a. Monoid a => [a] -> a
    mconcat ([Series] -> Series) -> (Posting -> [Series]) -> Posting -> Series
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Posting -> [Series]
    forall e kv. KeyValue e kv => Posting -> [kv]
    postingKV

    postingKV ::
    #if MIN_VERSION_aeson(2,2,0)
      KeyValue e kv
    #else
      KeyValue kv
    #endif
      => Posting -> [kv]
    postingKV :: forall e kv. KeyValue e kv => Posting -> [kv]
    postingKV Posting{[Tag]
    Maybe Day
    Maybe Transaction
    Maybe Posting
    Maybe BalanceAssertion
    Text
    Status
    PostingType
    MixedAmount
    pdate :: Maybe Day
    pdate2 :: Maybe Day
    pstatus :: Status
    paccount :: Text
    pamount :: MixedAmount
    pcomment :: Text
    ptype :: PostingType
    ptags :: [Tag]
    pbalanceassertion :: Maybe BalanceAssertion
    ptransaction :: Maybe Transaction
    poriginal :: Maybe Posting
    poriginal :: Posting -> Maybe Posting
    ptransaction :: Posting -> Maybe Transaction
    pbalanceassertion :: Posting -> Maybe BalanceAssertion
    ptags :: Posting -> [Tag]
    ptype :: Posting -> PostingType
    pcomment :: Posting -> Text
    pamount :: Posting -> MixedAmount
    paccount :: Posting -> Text
    pstatus :: Posting -> Status
    pdate2 :: Posting -> Maybe Day
    pdate :: Posting -> Maybe Day
    ..} =
        [ Key
    "pdate"             Key -> Maybe Day -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Maybe Day
    pdate
        , Key
    "pdate2"            Key -> Maybe Day -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Maybe Day
    pdate2
        , Key
    "pstatus"           Key -> Status -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Status
    pstatus
        , Key
    "paccount"          Key -> Text -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Text
    paccount
        , Key
    "pamount"           Key -> MixedAmount -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= MixedAmount
    pamount
        , Key
    "pcomment"          Key -> Text -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Text
    pcomment
        , Key
    "ptype"             Key -> PostingType -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= PostingType
    ptype
        , Key
    "ptags"             Key -> [Tag] -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= [Tag]
    ptags
        , Key
    "pbalanceassertion" Key -> Maybe BalanceAssertion -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Maybe BalanceAssertion
    pbalanceassertion
        -- To avoid a cycle, show just the parent transaction's index number
        -- in a dummy field. When re-parsed, there will be no parent.
        , Key
    "ptransaction_"     Key -> FilePath -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= FilePath
    -> (Transaction -> FilePath) -> Maybe Transaction -> FilePath
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe FilePath
    "" (Integer -> FilePath
    forall a. Show a => a -> FilePath
    show(Integer -> FilePath)
    -> (Transaction -> Integer) -> Transaction -> FilePath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    .Transaction -> Integer
    tindex) Maybe Transaction
    ptransaction
        -- This is probably not wanted in json, we discard it.
        , Key
    "poriginal"         Key -> Maybe Posting -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= (Maybe Posting
    forall a. Maybe a
    Nothing :: Maybe Posting)
        ]

    instance ToJSON Transaction
    instance ToJSON TransactionModifier
    instance ToJSON TMPostingRule
    instance ToJSON PeriodicTransaction
    instance ToJSON PriceDirective
    instance ToJSON EFDay
    instance ToJSON DateSpan
    instance ToJSON Interval
    instance ToJSON Period
    instance ToJSON AccountAlias
    instance ToJSON AccountType
    instance ToJSONKey AccountType
    instance ToJSON AccountDeclarationInfo
    instance ToJSON PayeeDeclarationInfo
    instance ToJSON TagDeclarationInfo
    instance ToJSON Commodity
    instance ToJSON TimeclockCode
    instance ToJSON TimeclockEntry
    instance ToJSON Journal

    instance ToJSON BalanceData
    instance ToJSON a => ToJSON (PeriodData a) where
      toJSON :: PeriodData a -> Value
    toJSON PeriodData a
    a = [Pair] -> Value
    object
        [ Key
    "pdpre" Key -> a -> Pair
    forall v. ToJSON v => Key -> v -> Pair
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= PeriodData a -> a
    forall a. PeriodData a -> a
    pdpre PeriodData a
    a
        , Key
    "pdperiods" Key -> [(Day, a)] -> Pair
    forall v. ToJSON v => Key -> v -> Pair
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= (Map Day a -> [(Day, a)]
    forall k a. Map k a -> [(k, a)]
    M.toList (Map Day a -> [(Day, a)]) -> Map Day a -> [(Day, a)]
    forall a b. (a -> b) -> a -> b
    $ PeriodData a -> Map Day a
    forall a. PeriodData a -> Map Day a
    pdperiods PeriodData a
    a)
        ]

    instance ToJSON a => ToJSON (Account a) where
      toJSON :: Account a -> Value
    toJSON = [Pair] -> Value
    object ([Pair] -> Value) -> (Account a -> [Pair]) -> Account a -> Value
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Account a -> [Pair]
    forall e kv a. (KeyValue e kv, ToJSON a) => Account a -> [kv]
    accountKV
      toEncoding :: Account a -> Encoding
    toEncoding = Series -> Encoding
    pairs (Series -> Encoding)
    -> (Account a -> Series) -> Account a -> Encoding
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Series] -> Series
    forall a. Monoid a => [a] -> a
    mconcat ([Series] -> Series)
    -> (Account a -> [Series]) -> Account a -> Series
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Account a -> [Series]
    forall e kv a. (KeyValue e kv, ToJSON a) => Account a -> [kv]
    accountKV

    accountKV ::
    #if MIN_VERSION_aeson(2,2,0)
      (KeyValue e kv, ToJSON a)
    #else
      (KeyValue kv, ToJSON a)
    #endif
      => Account a -> [kv]
    accountKV :: forall e kv a. (KeyValue e kv, ToJSON a) => Account a -> [kv]
    accountKV Account a
    a =
        [ Key
    "aname"            Key -> Text -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Account a -> Text
    forall a. Account a -> Text
    aname Account a
    a
        , Key
    "adeclarationinfo" Key -> Maybe AccountDeclarationInfo -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Account a -> Maybe AccountDeclarationInfo
    forall a. Account a -> Maybe AccountDeclarationInfo
    adeclarationinfo Account a
    a
        -- To avoid a cycle, show just the parent account's name
        -- in a dummy field. When re-parsed, there will be no parent.
        , Key
    "aparent_"         Key -> Text -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Text -> (Account a -> Text) -> Maybe (Account a) -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" Account a -> Text
    forall a. Account a -> Text
    aname (Account a -> Maybe (Account a)
    forall a. Account a -> Maybe (Account a)
    aparent Account a
    a)
        -- Just the names of subaccounts, as a dummy field, ignored when parsed.
        , Key
    "asubs_"           Key -> [Text] -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= (Account a -> Text) -> [Account a] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Account a -> Text
    forall a. Account a -> Text
    aname (Account a -> [Account a]
    forall a. Account a -> [Account a]
    asubs Account a
    a)
        -- The actual subaccounts (and their subs..), making a (probably highly redundant) tree
        -- ,"asubs"        .= asubs a
        -- Omit the actual subaccounts
        , Key
    "asubs"            Key -> [Account BalanceData] -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= ([]::[Account BalanceData])
        , Key
    "aboring"          Key -> Bool -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Account a -> Bool
    forall a. Account a -> Bool
    aboring Account a
    a
        , Key
    "adata"            Key -> PeriodData a -> kv
    forall v. ToJSON v => Key -> v -> kv
    forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
    .= Account a -> PeriodData a
    forall a. Account a -> PeriodData a
    adata Account a
    a
        ]

    instance ToJSON Ledger

    -- From JSON

    instance FromJSON Status
    instance FromJSON SourcePos
    -- Use the same encoding as the underlying Int
    instance FromJSON Pos where
      parseJSON :: Value -> Parser Pos
    parseJSON = (Int -> Pos) -> Parser Int -> Parser Pos
    forall a b. (a -> b) -> Parser a -> Parser b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Int -> Pos
    mkPos (Parser Int -> Parser Pos)
    -> (Value -> Parser Int) -> Value -> Parser Pos
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Value -> Parser Int
    forall a. FromJSON a => Value -> Parser a
    parseJSON

    instance FromJSON CostBasis
    instance FromJSON Amount
    instance FromJSON Rounding
    instance FromJSON AmountStyle

    -- Use the same JSON serialisation as Maybe Word8
    instance FromJSON AmountPrecision where
      parseJSON :: Value -> Parser AmountPrecision
    parseJSON = (Maybe Word8 -> AmountPrecision)
    -> Parser (Maybe Word8) -> Parser AmountPrecision
    forall a b. (a -> b) -> Parser a -> Parser b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (AmountPrecision
    -> (Word8 -> AmountPrecision) -> Maybe Word8 -> AmountPrecision
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe AmountPrecision
    NaturalPrecision Word8 -> AmountPrecision
    Precision) (Parser (Maybe Word8) -> Parser AmountPrecision)
    -> (Value -> Parser (Maybe Word8))
    -> Value
    -> Parser AmountPrecision
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Value -> Parser (Maybe Word8)
    forall a. FromJSON a => Value -> Parser a
    parseJSON

    instance FromJSON Side
    instance FromJSON DigitGroupStyle

    instance FromJSON MixedAmount where
      parseJSON :: Value -> Parser MixedAmount
    parseJSON = ([Amount] -> MixedAmount) -> Parser [Amount] -> Parser MixedAmount
    forall a b. (a -> b) -> Parser a -> Parser b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ([Amount] -> MixedAmount
    forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
    mixed :: [Amount] -> MixedAmount) (Parser [Amount] -> Parser MixedAmount)
    -> (Value -> Parser [Amount]) -> Value -> Parser MixedAmount
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Value -> Parser [Amount]
    forall a. FromJSON a => Value -> Parser a
    parseJSON

    instance FromJSON BalanceAssertion
    instance FromJSON AmountCost
    instance FromJSON MarketPrice
    instance FromJSON PostingType
    instance FromJSON Posting
    instance FromJSON Transaction
    instance FromJSON AccountDeclarationInfo

    instance FromJSON BalanceData
    instance FromJSON a => FromJSON (PeriodData a) where
      parseJSON :: Value -> Parser (PeriodData a)
    parseJSON = FilePath
    -> (Object -> Parser (PeriodData a))
    -> Value
    -> Parser (PeriodData a)
    forall a. FilePath -> (Object -> Parser a) -> Value -> Parser a
    withObject FilePath
    "PeriodData" ((Object -> Parser (PeriodData a))
     -> Value -> Parser (PeriodData a))
    -> (Object -> Parser (PeriodData a))
    -> Value
    -> Parser (PeriodData a)
    forall a b. (a -> b) -> a -> b
    $ \Object
    v -> a -> Map Day a -> PeriodData a
    forall a. a -> Map Day a -> PeriodData a
    PeriodData
        (a -> Map Day a -> PeriodData a)
    -> Parser a -> Parser (Map Day a -> PeriodData a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Object
    v Object -> Key -> Parser a
    forall a. FromJSON a => Object -> Key -> Parser a
    .: Key
    "pdpre"
        Parser (Map Day a -> PeriodData a)
    -> Parser (Map Day a) -> Parser (PeriodData a)
    forall a b. Parser (a -> b) -> Parser a -> Parser b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> ([(Day, a)] -> Map Day a
    forall k a. Ord k => [(k, a)] -> Map k a
    M.fromList ([(Day, a)] -> Map Day a)
    -> Parser [(Day, a)] -> Parser (Map Day a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Object
    v Object -> Key -> Parser [(Day, a)]
    forall a. FromJSON a => Object -> Key -> Parser a
    .: Key
    "pdperiods")

    -- XXX The ToJSON instance replaces subaccounts with just names.
    -- Here we should try to make use of those to reconstruct the
    -- parent-child relationships.
    instance FromJSON a => FromJSON (Account a)

    -- Decimal, various attempts
    --
    -- https://stackoverflow.com/questions/40331851/haskell-data-decimal-as-aeson-type
    ----instance FromJSON Decimal where parseJSON =
    ----  A.withScientific "Decimal" (return . right . eitherFromRational . toRational)
    --
    -- https://github.com/bos/aeson/issues/474
    -- http://hackage.haskell.org/package/aeson-1.4.2.0/docs/Data-Aeson-TH.html
    -- $(deriveFromJSON defaultOptions ''Decimal) -- doesn't work
    -- $(deriveFromJSON defaultOptions ''DecimalRaw)  -- works; requires TH, but gives better parse error messages
    --
    -- https://github.com/PaulJohnson/Haskell-Decimal/issues/6
    instance FromJSON (DecimalRaw Integer)
    --
    -- @simonmichael, I think the code in your first comment should work if it compiles—though “work” doesn’t mean you can parse a JSON number directly into a `Decimal` using the generic instance, as you’ve discovered.
    --
    --Error messages with these extensions are always rather cryptic, but I’d prefer them to Template Haskell. Typically you’ll want to start by getting a generic `ToJSON` instance working, then use that to figure out what the `FromJSON` instance expects to parse: for a correct instance, `encode` and `decode` should give you an isomorphism between your type and a subset of `Bytestring` (up to the `Maybe` wrapper that `decode` returns).
    --
    --I don’t have time to test it right now, but I think it will also work without `DeriveAnyClass`, just using `DeriveGeneric` and `StandAloneDeriving`. It should also work to use the [`genericParseJSON`](http://hackage.haskell.org/package/aeson/docs/Data-Aeson.html#v:genericParseJSON) function to implement the class explicitly, something like this:
    --
    --{-# LANGUAGE DeriveGeneric #-}
    --{-# LANGUAGE StandAloneDeriving #-}
    --import GHC.Generics
    --import Data.Aeson
    --deriving instance Generic Decimal
    --instance FromJSON Decimal where
    --  parseJSON = genericParseJSON defaultOptions
    --
    --And of course you can avoid `StandAloneDeriving` entirely if you’re willing to wrap `Decimal` in your own `newtype`.

    -- XXX these will allow reading a Journal, but currently the
    -- jdeclaredaccounttypes Map gets serialised as a JSON list, which
    -- can't be read back.
    --
    -- instance FromJSON AccountAlias
    -- instance FromJSONKey AccountType where fromJSONKey = genericFromJSONKey defaultJSONKeyOptions
    -- instance FromJSON AccountType
    -- instance FromJSON ClockTime
    -- instance FromJSON Commodity
    -- instance FromJSON DateSpan
    -- instance FromJSON Interval
    -- instance FromJSON Period
    -- instance FromJSON PeriodicTransaction
    -- instance FromJSON PriceDirective
    -- instance FromJSON TimeclockCode
    -- instance FromJSON TimeclockEntry
    -- instance FromJSON TransactionModifier
    -- instance FromJSON Journal

    -- Utilities

    -- | Config for pretty printing JSON output.
    jsonConf :: Config
    jsonConf :: Config
    jsonConf = Config{confIndent :: Indent
    confIndent=Int -> Indent
    Spaces Int
    2, confCompare :: Text -> Text -> Ordering
    confCompare=Text -> Text -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare, confNumFormat :: NumberFormat
    confNumFormat=NumberFormat
    Generic, confTrailingNewline :: Bool
    confTrailingNewline=Bool
    True}

    -- | Show a JSON-convertible haskell value as pretty-printed JSON text.
    toJsonText :: ToJSON a => a -> TL.Text
    toJsonText :: forall a. ToJSON a => a -> Text
    toJsonText = Builder -> Text
    TB.toLazyText (Builder -> Text) -> (a -> Builder) -> a -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Config -> a -> Builder
    forall a. ToJSON a => Config -> a -> Builder
    encodePrettyToTextBuilder' Config
    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 :: forall a. ToJSON a => FilePath -> a -> IO ()
    writeJsonFile FilePath
    f = FilePath -> ByteString -> IO ()
    BL.writeFile FilePath
    f (ByteString -> IO ()) -> (a -> ByteString) -> a -> IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Config -> a -> ByteString
    forall a. ToJSON a => Config -> a -> ByteString
    encodePretty' Config
    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
    readJsonFile :: FromJSON a => FilePath -> IO a
    readJsonFile :: forall a. FromJSON a => FilePath -> IO a
    readJsonFile FilePath
    f = do
      bl <- FilePath -> IO ByteString
    BL.readFile FilePath
    f
      -- PARTIAL:
      let v = Value -> Maybe Value -> Value
    forall a. a -> Maybe a -> a
    fromMaybe (FilePath -> Value
    forall a. FilePath -> a
    error' (FilePath -> Value) -> FilePath -> Value
    forall a b. (a -> b) -> a -> b
    $ FilePath
    "could not decode JSON in "FilePath -> FilePath -> FilePath
    forall a. [a] -> [a] -> [a]
    ++FilePath -> FilePath
    forall a. Show a => a -> FilePath
    show FilePath
    fFilePath -> FilePath -> FilePath
    forall a. [a] -> [a] -> [a]
    ++FilePath
    " to target value")
              (ByteString -> Maybe Value
    forall a. FromJSON a => ByteString -> Maybe a
    decode ByteString
    bl :: Maybe Value)
      case fromJSON v :: FromJSON a => Result a of
        Error FilePath
    e   -> FilePath -> IO a
    forall a. FilePath -> a
    error' FilePath
    e
        Success a
    t -> a -> IO a
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    t