File: Aeson.hs

package info (click to toggle)
git-annex 7.20190129-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 56,292 kB
  • sloc: haskell: 59,105; sh: 1,255; makefile: 225; perl: 136; ansic: 44
file content (104 lines) | stat: -rw-r--r-- 3,086 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{- GHC File system encoding support for Aeson.
 -
 - Import instead of Data.Aeson
 -
 - Copyright 2018-2019 Joey Hess <id@joeyh.name>
 -
 - License: BSD-2-clause
 -}

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}

module Utility.Aeson (
	module X,
	ToJSON'(..),
	encode,
	packString,
	packByteString,
) where

import Data.Aeson as X hiding (ToJSON, toJSON, encode)
import Data.Aeson hiding (encode)
import qualified Data.Aeson
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString as S
import qualified Data.Set
import qualified Data.Vector
import Prelude

import Utility.FileSystemEncoding

-- | Use this instead of Data.Aeson.encode to make sure that the
-- below String instance is used.
encode :: ToJSON' a => a -> L.ByteString
encode = Data.Aeson.encode . toJSON'

-- | Aeson has an unfortunate ToJSON instance for Char and [Char]
-- which does not support Strings containing UTF8 characters
-- encoded using the filesystem encoding when run in a non-utf8 locale.
--
-- Since we can't replace that with a instance that does the right
-- thing, instead here's a new class that handles String right.
class ToJSON' a where
	toJSON' :: a -> Value

instance ToJSON' T.Text where
	toJSON' = toJSON

instance ToJSON' String where
	toJSON' = toJSON . packString

-- | Aeson does not have a ToJSON instance for ByteString;
-- this one assumes that the ByteString contains text, and will
-- have the same effect as toJSON' . decodeBS, but with a more efficient
-- implementation.
instance ToJSON' S.ByteString where
	toJSON' = toJSON . packByteString

-- | Pack a String to Text, correctly handling the filesystem encoding.
--
-- Use this instead of Data.Text.pack.
--
-- Note that if the string contains invalid UTF8 characters not using
-- the FileSystemEncoding, this is the same as Data.Text.pack.
packString :: String -> T.Text
packString s = case T.decodeUtf8' (encodeBS s) of
	Right t -> t
	Left _ -> T.pack s

-- | The same as packString . decodeBS, but more efficient in the usual
-- case.
packByteString :: S.ByteString -> T.Text
packByteString b = case T.decodeUtf8' b of
	Right t -> t
	Left _ -> T.pack (decodeBS b)

-- | An instance for lists cannot be included as it would overlap with
-- the String instance. Instead, you can use a Vector.
instance ToJSON' s => ToJSON' (Data.Vector.Vector s) where
	toJSON' = toJSON . map toJSON' . Data.Vector.toList

-- Aeson generates the same JSON for a Set as for a list.
instance ToJSON' s => ToJSON' (Data.Set.Set s) where
	toJSON' = toJSON . map toJSON' . Data.Set.toList

instance (ToJSON' a, ToJSON a) => ToJSON' (Maybe a) where
	toJSON' (Just a) = toJSON (Just (toJSON' a))
	toJSON' v@Nothing = toJSON v

instance (ToJSON' a, ToJSON a, ToJSON' b, ToJSON b) => ToJSON' (a, b) where
	toJSON' (a, b) = toJSON ((toJSON' a, toJSON' b))

instance ToJSON' Bool where
	toJSON' = toJSON

instance ToJSON' Integer where
	toJSON' = toJSON

instance ToJSON' Object where
	toJSON' = toJSON

instance ToJSON' Value where
	toJSON' = toJSON