Skip to content

Commit c9e053d

Browse files
authored
Merge pull request #1 from garyb/jarray
Introduce `JArray`
2 parents 4028664 + 7f4e6de commit c9e053d

File tree

10 files changed

+181
-93
lines changed

10 files changed

+181
-93
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# purescript-json
2+
3+
[![Build status](https://github.com/purescript/purescript-json/workflows/CI/badge.svg?branch=master)](https://github.com/purescript/purescript-json/actions?query=workflow%3ACI+branch%3Amaster)
4+
5+
Standard types and basic operations for working with JSON.
6+
7+
For efficiency and performance reasons this library provides an interface for working with JSON without using PureScript ADTs, and instead operates on the underlying representation.
8+
9+
## Differences from Argonaut
10+
11+
This library is similar to the traditionally used `argonaut-core` library, but has been implemented with an eye to making it backend agnostic. As such, it does not use `Foreign.Object` as the representation for JSON objects, does not use `Array JSON`, and instead provides its own `JObject` and `JArray` types.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
"private": true,
33
"scripts": {
44
"clean": "rimraf output && rimraf .pulp-cache",
5-
"build": "eslint src && purs-tidy check --config-require src/**/*.purs && pulp build -- --censor-lib --strict"
5+
"build": "eslint src && purs-tidy check --config-require src/**/*.purs && pulp build -- --censor-lib --strict",
6+
"test": "pulp test"
67
},
78
"devDependencies": {
8-
"eslint": "^8.25.0",
9+
"eslint": "^8.40.0",
910
"pulp": "^16.0.2",
1011
"purescript-psa": "^0.8.2",
11-
"purs-tidy": "^0.9.2",
12-
"rimraf": "^3.0.2"
12+
"purs-tidy": "^0.9.3",
13+
"rimraf": "^5.0.0"
1314
}
1415
}

src/JSON.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ export const fromInt = coerce;
88

99
export const fromString = coerce;
1010

11-
export const fromArray = coerce;
11+
export const fromJArray = coerce;
1212

13-
export const fromObject = coerce;
13+
export const fromJObject = coerce;
1414

1515
export const print = (j) => JSON.stringify(j);
1616

src/JSON.purs

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,69 @@ module JSON
22
( parse
33
, null
44
, fromBoolean
5-
, fromNumberWithDefault
65
, fromNumber
6+
, fromNumberWithDefault
77
, fromInt
88
, fromString
99
, fromArray
10-
, fromObject
10+
, fromJArray
11+
, fromJObject
1112
, case_
1213
, toNull
1314
, toBoolean
1415
, toNumber
1516
, toString
1617
, toArray
17-
, toObject
18+
, toJArray
19+
, toJObject
1820
, print
1921
, printIndented
20-
, module Internal
22+
, module Exports
2123
) where
2224

2325
import Prelude
2426

2527
import Data.Either (Either(..))
2628
import Data.Function.Uncurried (runFn2, runFn3, runFn7)
2729
import Data.Maybe (Maybe(..))
28-
import JSON.Internal (JSON) as Internal
29-
import JSON.Internal (JSON, Object, _case, _fromNumberWithDefault, _parse)
30+
import JSON.Internal (JArray, JObject, JSON) as Exports
31+
import JSON.Internal (JArray, JObject, JSON)
32+
import JSON.Internal as Internal
3033

3134
-- | Attempts to parse a string as a JSON value. If parsing fails, an error message detailing the
3235
-- | cause may be returned in the `Left` of the result.
3336
parse :: String -> Either String JSON
34-
parse j = runFn3 _parse Left Right j
37+
parse j = runFn3 Internal._parse Left Right j
3538

3639
-- | The JSON `null` value.
3740
null :: JSON
3841
null = _null
3942

43+
-- | The JSON `null` value.
4044
foreign import _null :: JSON
4145

42-
-- | Creates a `JSON` value from a `Boolean`.
46+
-- | Converts a `Boolean` into `JSON`.
4347
foreign import fromBoolean :: Boolean -> JSON
4448

45-
-- | Creates a `JSON` value from a `Number`, using a fallback `Int` value for cases where the
46-
-- | PureScript number value is not valid for JSON.
47-
fromNumberWithDefault :: Int -> Number -> JSON
48-
fromNumberWithDefault fallback n = runFn2 _fromNumberWithDefault fallback n
49-
50-
-- | Creates a `JSON` value from a `Number`.
49+
-- | Converts a `Number` into `JSON`.
5150
-- |
5251
-- | The PureScript `Number` type admits infinities and a `NaN` value which are not allowed in JSON,
5352
-- | so when encountered, this function will treat those values as 0.
5453
fromNumber :: Number -> JSON
55-
fromNumber n = runFn2 _fromNumberWithDefault 0 n
54+
fromNumber n = runFn2 Internal._fromNumberWithDefault 0 n
5655

57-
-- | Creates a `JSON` value from an `Int`.
56+
-- | Creates a `Number` into `JSON`, using a fallback `Int` value for cases where the
57+
-- | PureScript number value is not valid for JSON (`NaN`, `infinity`).
58+
fromNumberWithDefault :: Int -> Number -> JSON
59+
fromNumberWithDefault fallback n = runFn2 Internal._fromNumberWithDefault fallback n
60+
61+
-- | Converts an `Int` into `JSON`.
5862
-- |
5963
-- | There is no corresponding `toInt` as JSON doesn't have a concept of integers - this is provided
6064
-- | as a convenience to avoid having to convert `Int` to `Number` before creating a `JSON` value.
6165
foreign import fromInt :: Int -> JSON
6266

63-
-- | Creates a `JSON` value from a `String`.
67+
-- | Converts a `String` into `JSON`.
6468
-- |
6569
-- | **Note**: this does not parse a string as a JSON value, it takes a PureScript string and
6670
-- | produces the corresponding `JSON` value for that string, similar to the other functions like
@@ -70,59 +74,69 @@ foreign import fromInt :: Int -> JSON
7074
-- | [`parse`](#v:parse).
7175
foreign import fromString :: String -> JSON
7276

73-
-- | Creates a `JSON` value from an array of `JSON` values.
74-
foreign import fromArray :: Array JSON -> JSON
77+
-- | Converts a `JArray` into `JSON`.
78+
foreign import fromJArray :: JArray -> JSON
79+
80+
-- | Converts an array of `JSON` values into `JSON`.
81+
fromArray :: Array JSON -> JSON
82+
fromArray js = fromJArray (Internal.fromArray js)
7583

76-
-- | Creates a `JSON` value from an `Object`.
77-
foreign import fromObject :: Object -> JSON
84+
-- | Converts a `JObject` into `JSON`.
85+
foreign import fromJObject :: JObject -> JSON
7886

7987
-- | Performs case analysis on a JSON value.
8088
-- |
8189
-- | As the `JSON` type is not a PureScript sum type, pattern matching cannot be used to
8290
-- | discriminate between the potential varieties of value. This function provides an equivalent
8391
-- | mechanism by accepting functions that deal with each variety, similar to an exaustive `case`
8492
-- | statement.
93+
-- |
94+
-- | The `Unit` case is for `null` values.
8595
case_
8696
:: forall a
8797
. (Unit -> a)
8898
-> (Boolean -> a)
8999
-> (Number -> a)
90100
-> (String -> a)
91-
-> (Array JSON -> a)
92-
-> (Object -> a)
101+
-> (JArray -> a)
102+
-> (JObject -> a)
93103
-> JSON
94104
-> a
95-
case_ a b c d e f json = runFn7 _case a b c d e f json
105+
case_ a b c d e f json = runFn7 Internal._case a b c d e f json
96106

97107
fail :: forall a b. a -> Maybe b
98108
fail _ = Nothing
99109

100110
-- | Converts a `JSON` value to `Null` if the `JSON` is `null`.
101111
toNull :: JSON -> Maybe Unit
102-
toNull json = runFn7 _case Just fail fail fail fail fail json
112+
toNull json = runFn7 Internal._case Just fail fail fail fail fail json
103113

104114
-- | Converts a `JSON` value to `Boolean` if the `JSON` is a boolean.
105115
toBoolean :: JSON -> Maybe Boolean
106-
toBoolean json = runFn7 _case fail Just fail fail fail fail json
116+
toBoolean json = runFn7 Internal._case fail Just fail fail fail fail json
107117

108118
-- | Converts a `JSON` value to `Number` if the `JSON` is a number.
109119
toNumber :: JSON -> Maybe Number
110-
toNumber json = runFn7 _case fail fail Just fail fail fail json
120+
toNumber json = runFn7 Internal._case fail fail Just fail fail fail json
111121

112122
-- | Converts a `JSON` value to `String` if the `JSON` is a string.
113123
toString :: JSON -> Maybe String
114-
toString json = runFn7 _case fail fail fail Just fail fail json
124+
toString json = runFn7 Internal._case fail fail fail Just fail fail json
125+
126+
-- | Converts a `JSON` value to `JArray` if the `JSON` is an array.
127+
toJArray :: JSON -> Maybe JArray
128+
toJArray json = runFn7 Internal._case fail fail fail fail Just fail json
115129

116130
-- | Converts a `JSON` value to `Array JSON` if the `JSON` is an array.
117131
toArray :: JSON -> Maybe (Array JSON)
118-
toArray json = runFn7 _case fail fail fail fail Just fail json
132+
toArray json = Internal.toArray <$> toJArray json
119133

120134
-- | Converts a `JSON` value to `Object` if the `JSON` is an object.
121-
toObject :: JSON -> Maybe Object
122-
toObject json = runFn7 _case fail fail fail fail fail Just json
135+
toJObject :: JSON -> Maybe JObject
136+
toJObject json = runFn7 Internal._case fail fail fail fail fail Just json
123137

124138
-- | Prints a JSON value as a compact (single line) string.
125139
foreign import print :: JSON -> String
126140

127-
-- | Prints a JSON value as a "pretty" string,
141+
-- | Prints a JSON value as a "pretty" string with newlines and indentation.
128142
foreign import printIndented :: JSON -> String

src/JSON/Array.purs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module JSON.Array
2+
( fromFoldable
3+
, empty
4+
, singleton
5+
, index
6+
, toUnfoldable
7+
, module Exports
8+
) where
9+
10+
import Data.Array as Array
11+
import Data.Foldable (class Foldable)
12+
import Data.Maybe (Maybe)
13+
import Data.Unfoldable (class Unfoldable)
14+
import JSON.Internal (JArray, JSON, toArray, fromArray)
15+
import JSON.Internal (JArray, toArray, fromArray) as Exports
16+
17+
-- | Creates a `JArray` from a `Foldable` source of `JSON`.
18+
fromFoldable :: forall f. Foldable f => f JSON -> JArray
19+
fromFoldable js = fromArray (Array.fromFoldable js)
20+
21+
-- | An empty `JArray`.
22+
empty :: JArray
23+
empty = fromArray []
24+
25+
-- | Creates a `JArray` with a single entry.
26+
singleton :: JSON -> JArray
27+
singleton j = fromArray [ j ]
28+
29+
-- | Attempts to read a value from the specified index of a `JArray`.
30+
index :: JArray -> Int -> Maybe JSON
31+
index js = Array.index (toArray js)
32+
33+
-- | Unfolds a `JArray` into `JSON` items
34+
toUnfoldable :: forall f. Unfoldable f => JArray -> f JSON
35+
toUnfoldable js = Array.toUnfoldable (toArray js)

src/JSON/Gen.purs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import Data.NonEmpty ((:|))
1010
import Data.String.Gen (genUnicodeString)
1111
import Data.Tuple (Tuple(..))
1212
import JSON as J
13-
import JSON.Object as Object
13+
import JSON.Array as JArray
14+
import JSON.Object as JObject
1415

1516
-- | A generator for random `JSON` values of any variety.
1617
genJSON :: forall m. MonadGen m => MonadRec m => Lazy (m J.JSON) => m J.JSON
@@ -23,15 +24,15 @@ genJSON = Gen.resize (min 5) $ Gen.sized genJSON'
2324

2425
-- | A generator for JSON arrays containing items based on the passed generator.
2526
genArrayOf :: forall m. MonadGen m => MonadRec m => m J.JSON -> m J.JSON
26-
genArrayOf inner = J.fromArray <$> Gen.unfoldable inner
27+
genArrayOf inner = J.fromJArray <<< JArray.fromArray <$> Gen.unfoldable inner
2728

2829
-- | A generator for JSON arrays containing random items.
2930
genArray :: forall m. MonadGen m => MonadRec m => Lazy (m J.JSON) => m J.JSON
3031
genArray = genArrayOf (defer \_ -> genJSON)
3132

3233
-- | A generator for JSON objects containing entries based on the passed generator.
3334
genObjectOf :: forall m. MonadGen m => MonadRec m => m (Tuple String J.JSON) -> m J.JSON
34-
genObjectOf inner = J.fromObject <<< Object.fromEntries <$> (Gen.unfoldable inner)
35+
genObjectOf inner = J.fromJObject <<< JObject.fromEntries <$> (Gen.unfoldable inner)
3536

3637
-- | A generator for JSON objects containing random entries.
3738
genObject :: forall m. MonadGen m => MonadRec m => Lazy (m J.JSON) => m J.JSON

src/JSON/Internal.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export const _case = (isNull, isBool, isNum, isStr, isArr, isObj, j) => {
2222
return isObj(j);
2323
};
2424

25+
export const toArray = (js) => js;
26+
export const fromArray = (js) => js;
27+
2528
export const _fromEntries = (fst, snd, entries) => {
2629
const result = {};
2730
for (var i = 0; i < entries.length; i++) {

src/JSON/Internal.purs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,36 @@ _lt _ = LT
4444
_gt :: forall a. a -> Ordering
4545
_gt _ = GT
4646

47+
-- | A type that represents JSON arrays. Similar to the JSON type, this is not a PureScript type,
48+
-- | but represents the underlying representation for JSON arrays.
49+
foreign import data JArray :: Type
50+
51+
-- | Converts a `JArray` into an `Array` of `JSON` values
52+
foreign import toArray :: JArray -> Array JSON
53+
54+
-- | Converts an `Array` of `JSON` values into a `JArray`.
55+
foreign import fromArray :: Array JSON -> JArray
56+
57+
instance Eq JArray where
58+
eq x y = eq (toArray x) (toArray y)
59+
60+
instance Ord JArray where
61+
compare x y = compare (toArray x) (toArray y)
62+
63+
instance Semigroup JArray where
64+
append x y = fromArray (append (toArray x) (toArray y))
65+
66+
instance Monoid JArray where
67+
mempty = fromArray []
68+
4769
-- | A type that represents JSON objects. Similar to the JSON type, this is not a PureScript type,
4870
-- | but represents the underlying representation for JSON objects.
49-
foreign import data Object :: Type
71+
foreign import data JObject :: Type
5072

51-
instance Eq Object where
73+
instance Eq JObject where
5274
eq x y = eq (runFn2 _entries Tuple x) (runFn2 _entries Tuple y)
5375

54-
instance Ord Object where
76+
instance Ord JObject where
5577
compare x y = compare (runFn2 _entries Tuple x) (runFn2 _entries Tuple y)
5678

5779
foreign import _parse
@@ -70,28 +92,28 @@ foreign import _case
7092
(Boolean -> a)
7193
(Number -> a)
7294
(String -> a)
73-
(Array JSON -> a)
74-
(Object -> a)
95+
(JArray -> a)
96+
(JObject -> a)
7597
JSON
7698
a
7799

78-
foreign import _insert :: Fn3 String JSON Object Object
100+
foreign import _insert :: Fn3 String JSON JObject JObject
79101

80-
foreign import _delete :: Fn2 String Object Object
102+
foreign import _delete :: Fn2 String JObject JObject
81103

82104
foreign import _fromEntries
83105
:: Fn3
84106
(forall x y. Tuple x y -> x)
85107
(forall x y. Tuple x y -> y)
86-
(Array (Tuple String JSON))
87-
Object
108+
(Prim.Array (Tuple String JSON))
109+
JObject
88110

89-
foreign import _entries :: forall c. Fn2 (String -> JSON -> c) Object (Array c)
111+
foreign import _entries :: forall c. Fn2 (String -> JSON -> c) JObject (Prim.Array c)
90112

91113
foreign import _lookup
92114
:: Fn4
93115
(forall a. Maybe a)
94116
(forall a. a -> Maybe a)
95117
String
96-
Object
118+
JObject
97119
(Maybe JSON)

0 commit comments

Comments
 (0)