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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
------------------------------------------------------------------------------
--- This library defines a type class and instances for standard types
--- to convert Curry values to JSON values and vice versa.
---
--- @author Michael Hanus
--- @version February 2025
------------------------------------------------------------------------------

module JSON.Convert where

import Data.Maybe ( catMaybes, isJust )
import JSON.Data

--- Type class with two conversion operations between values and their
--- JSON representation. Since a JSON value might not contain
--- a correct representation of a standard value, the operation
--- `fromJSON` returns a `Maybe` value.
--- The additional operations on value lists are used for a better
--- JSON conversion of strings.
class ConvertJSON a where
  toJSON :: a -> JValue
  fromJSON :: JValue -> Maybe a

  toJSONList :: [a] -> JValue
  fromJSONList :: JValue -> Maybe [a]

  toJSONList = JArray . map toJSON

  fromJSONList jv = case jv of
    JArray xs -> let ys = map fromJSON xs
                 in if all isJust ys then Just (catMaybes ys)
                                     else Nothing
    _         -> Nothing

--- Instance for Booleans.
instance ConvertJSON Bool where
  toJSON b = JBool b

  fromJSON jv = case jv of
    JBool b -> Just b
    _       -> Nothing

--- Instance for characters and strings.
instance ConvertJSON Char where
  toJSON c = JString [c]

  fromJSON jv = case jv of
    JString [c] -> Just c
    _           -> Nothing

  toJSONList s = JString s

  fromJSONList jv = case jv of
    JString s -> Just s
    _         -> Nothing

--- Instance for floats.
instance ConvertJSON Float where
  toJSON x = JNumber x

  fromJSON jv = case jv of
    JNumber n -> Just n
    _         -> Nothing

--- Instance for integers.
instance ConvertJSON Int where
  toJSON n = JInt n

  fromJSON jv = case jv of
    JInt n -> Just n
    _      -> Nothing

--- Instance for lists.
instance ConvertJSON a => ConvertJSON [a] where
  toJSON = toJSONList

  fromJSON = fromJSONList

--- Instance for `Maybe` values.
instance ConvertJSON a => ConvertJSON (Maybe a) where
  toJSON Nothing  = JNull
  toJSON (Just x) = toJSON x

  fromJSON jv = case jv of
    JNull -> Just Nothing
    _     -> fmap Just (fromJSON jv)

--- Instance for `Either` values.
instance (ConvertJSON a, ConvertJSON b) => ConvertJSON (Either a b) where
  toJSON (Left  x) = JObject $ toJObject [("Left",  toJSON x)]
  toJSON (Right y) = JObject $ toJObject [("Right", toJSON y)]

  fromJSON jv = case jv of
    JObject jo -> case fromJObject jo of
                    [("Left", v)] -> fmap Left  (fromJSON v)
                    [("Right",v)] -> fmap Right (fromJSON v)
                    _             -> Nothing
    _          -> Nothing

--- Instance for `Ordering` values.
instance ConvertJSON Ordering where
  toJSON x = JString (show x)

  fromJSON jv = case jv of
    JString s -> case reads s of [(x,"")] -> Just x
                                 _        -> Nothing
    _         -> Nothing

--- Instance for pairs of values.
instance (ConvertJSON a, ConvertJSON b) => ConvertJSON (a,b) where
  toJSON (x,y) = JObject $ toJObject [("1", toJSON x), ("2", toJSON y)]

  fromJSON jv = case jv of
    JObject jo -> case fromJObject jo of
                    [("1",v1), ("2",v2)] -> do x <- fromJSON v1
                                               y <- fromJSON v2
                                               return (x,y)
                    _                    -> Nothing
    _          -> Nothing