Skip to content

Commit

Permalink
Use optional args for JSON.stringify and JSON.parseExn
Browse files Browse the repository at this point in the history
  • Loading branch information
cknitt committed Mar 30, 2024
1 parent 6d2d3d8 commit 9a395a0
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next version

- Add optional arguments to `JSON.stringify` and deprecate `JSON.stringifyWithIndent`, `JSON.stringifyWithReplacer` etc. https://github.com/rescript-association/rescript-core/pull/201
- BREAKING: Intl types: simplify bindings for constructors / functions with optional arguments. https://github.com/rescript-association/rescript-core/pull/198
- Fix: Expose Intl.Common. https://github.com/rescript-association/rescript-core/pull/197
- Add `Array.flatMapWithIndex` https://github.com/rescript-association/rescript-core/pull/199
Expand Down
39 changes: 26 additions & 13 deletions src/Core__JSON.res
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,38 @@ type rec t = Js.Json.t =
| Object(Core__Dict.t<t>)
| Array(array<t>)

@raises @val external parseExn: string => t = "JSON.parse"
@raises @val external parseExnWithReviver: (string, (string, t) => t) => t = "JSON.parse"
@val external stringify: t => string = "JSON.stringify"
@val external stringifyWithIndent: (t, @as(json`null`) _, int) => string = "JSON.stringify"
@val external stringifyWithReplacer: (t, (string, t) => t) => string = "JSON.stringify"
@val
@unboxed
type replacer = Keys(array<string>) | Replacer((string, t) => t)

@raises @val external parseExn: (string, ~reviver: (string, t) => t=?) => t = "JSON.parse"
@deprecated("Use `parseExn` with optional parameter instead") @raises @val
external parseExnWithReviver: (string, (string, t) => t) => t = "JSON.parse"

@val external stringify: (t, ~replacer: replacer=?, ~space: int=?) => string = "JSON.stringify"
@deprecated("Use `stringify` with optional parameter instead") @val
external stringifyWithIndent: (t, @as(json`null`) _, int) => string = "JSON.stringify"
@deprecated("Use `stringify` with optional parameter instead") @val
external stringifyWithReplacer: (t, (string, t) => t) => string = "JSON.stringify"
@deprecated("Use `stringify` with optional parameters instead") @val
external stringifyWithReplacerAndIndent: (t, (string, t) => t, int) => string = "JSON.stringify"
@val external stringifyWithFilter: (t, array<string>) => string = "JSON.stringify"
@val external stringifyWithFilterAndIndent: (t, array<string>, int) => string = "JSON.stringify"
@raises @val external stringifyAny: 'a => option<string> = "JSON.stringify"
@deprecated("Use `stringify` with optional parameter instead") @val
external stringifyWithFilter: (t, array<string>) => string = "JSON.stringify"
@deprecated("Use `stringify` with optional parameters instead") @val
external stringifyWithFilterAndIndent: (t, array<string>, int) => string = "JSON.stringify"

@raises @val
external stringifyAny: ('a, ~replacer: replacer=?, ~space: int=?) => option<string> =
"JSON.stringify"
@deprecated("Use `stringifyAny` with optional parameter instead") @raises @val
external stringifyAnyWithIndent: ('a, @as(json`null`) _, int) => option<string> = "JSON.stringify"
@raises @val
@deprecated("Use `stringifyAny` with optional parameter instead") @raises @val
external stringifyAnyWithReplacer: ('a, (string, t) => t) => option<string> = "JSON.stringify"
@raises @val
@deprecated("Use `stringifyAny` with optional parameters instead") @raises @val
external stringifyAnyWithReplacerAndIndent: ('a, (string, t) => t, int) => option<string> =
"JSON.stringify"
@raises @val external stringifyAnyWithFilter: ('a, array<string>) => string = "JSON.stringify"
@raises @val
@deprecated("Use `stringifyAny` with optional parameter instead") @raises @val
external stringifyAnyWithFilter: ('a, array<string>) => string = "JSON.stringify"
@deprecated("Use `stringifyAny` with optional parameters instead") @raises @val
external stringifyAnyWithFilterAndIndent: ('a, array<string>, int) => string = "JSON.stringify"

module Classify = {
Expand Down
97 changes: 91 additions & 6 deletions src/Core__JSON.resi
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ type rec t = Js.Json.t =
| Object(Core__Dict.t<t>)
| Array(array<t>)

@unboxed
type replacer = Keys(array<string>) | Replacer((string, t) => t)

/**
`parseExn(string)`
`parseExn(string, ~reviver=?)`
Parses a JSON string or throws a JavaScript exception (SyntaxError), if the string isn't valid.
The reviver describes how the value should be transformed. It is a function which receives a key and a value.
It returns a JSON type.
## Examples
Expand All @@ -31,6 +35,28 @@ try {
} catch {
| Exn.Error(_) => Console.log("error")
}
let reviver = (_, value) => {
let valueType = JSON.Classify.classify(value)
switch valueType {
| String(string) => string->String.toUpperCase->JSON.Encode.string
| Number(number) => (number *. 2.0)->JSON.Encode.float
| _ => value
}
}
let jsonString = `{"hello":"world","someNumber":21}`
try {
JSON.parseExn(jsonString, ~reviver)->Console.log
// { hello: 'WORLD', someNumber: 42 }
JSON.parseExn("", ~reviver)->Console.log
// error
} catch {
| Exn.Error(_) => Console.log("error")
}
```
## Exceptions
Expand All @@ -39,7 +65,7 @@ try {
*/
@raises(Exn.t)
@val
external parseExn: string => t = "JSON.parse"
external parseExn: (string, ~reviver: (string, t) => t=?) => t = "JSON.parse"

/**
`parseExnWithReviver(string, reviver)`
Expand Down Expand Up @@ -77,14 +103,17 @@ try {
- Raises a SyntaxError if the string isn't valid JSON.
*/
@deprecated("Use `parseExn` with optional parameter instead")
@raises(Exn.t)
@val
external parseExnWithReviver: (string, (string, t) => t) => t = "JSON.parse"

/**
`stringify(json)`
`stringify(json, ~replacer=?, ~space=?)`
Converts a JSON object to a JSON string.
The replacer describes how the value should be transformed. It is a function which receives a key and a value,
or an array of keys which should be included in the output.
If you want to stringify any type, use `JSON.stringifyAny` instead.
## Examples
Expand All @@ -98,10 +127,32 @@ let json =
JSON.stringify(json)
// {"foo":"bar","hello":"world","someNumber":42}
JSON.stringify(json, ~space=2)
// {
// "foo": "bar",
// "hello": "world",
// "someNumber": 42
// }
JSON.stringify(json, ~replacer=Keys(["foo", "someNumber"]))
// {"foo":"bar","someNumber":42}
let replacer = Replacer((_, value) => {
let decodedValue = value->JSON.Decode.string
switch decodedValue {
| Some(string) => string->String.toUpperCase->JSON.Encode.string
| None => value
}
})
JSON.stringify(json, ~replacer)
// {"foo":"BAR","hello":"WORLD","someNumber":42}
```
*/
@val
external stringify: t => string = "JSON.stringify"
external stringify: (t, ~replacer: replacer=?, ~space: int=?) => string = "JSON.stringify"

/**
`stringifyWithIndent(json, indentation)`
Expand All @@ -126,6 +177,7 @@ JSON.stringifyWithIndent(json, 2)
// }
```
*/
@deprecated("Use `stringify` with optional parameter instead")
@val
external stringifyWithIndent: (t, @as(json`null`) _, int) => string = "JSON.stringify"

Expand Down Expand Up @@ -158,6 +210,7 @@ JSON.stringifyWithReplacer(json, replacer)
// {"foo":"BAR","hello":"WORLD","someNumber":42}
```
*/
@deprecated("Use `stringify` with optional parameter instead")
@val
external stringifyWithReplacer: (t, (string, t) => t) => string = "JSON.stringify"

Expand Down Expand Up @@ -194,6 +247,7 @@ JSON.stringifyWithReplacerAndIndent(json, replacer, 2)
// }
```
*/
@deprecated("Use `stringify` with optional parameters instead")
@val
external stringifyWithReplacerAndIndent: (t, (string, t) => t, int) => string = "JSON.stringify"

Expand All @@ -217,6 +271,7 @@ JSON.stringifyWithFilter(json, ["foo", "someNumber"])
// {"foo":"bar","someNumber":42}
```
*/
@deprecated("Use `stringify` with optional parameter instead")
@val
external stringifyWithFilter: (t, array<string>) => string = "JSON.stringify"

Expand All @@ -243,13 +298,15 @@ JSON.stringifyWithFilterAndIndent(json, ["foo", "someNumber"], 2)
// }
```
*/
@deprecated("Use `stringify` with optional parameters instead")
@val
external stringifyWithFilterAndIndent: (t, array<string>, int) => string = "JSON.stringify"

/**
`stringifyAny(any)`
`stringifyAny(any, ~replacer=?, ~space=?)`
Converts any type to a JSON string.
The replacer describes how the value should be transformed. It is a function which receives a key and a value.
Stringifying a function or `undefined` will return `None`.
If the value contains circular references or `BigInt`s, the function will throw a JavaScript exception (TypeError).
If you want to stringify a JSON object, use `JSON.stringify` instead.
Expand All @@ -265,6 +322,28 @@ let dict = Dict.fromArray([
JSON.stringifyAny(dict)
// {"foo":"bar","hello":"world","someNumber":42}
JSON.stringifyAny(dict, ~space=2)
// {
// "foo": "bar",
// "hello": "world",
// "someNumber": 42
// }
JSON.stringifyAny(dict, ~replacer=Keys(["foo", "someNumber"]))
// {"foo":"bar","someNumber":42}
let replacer = Replacer((_, value) => {
let decodedValue = value->JSON.Decode.string
switch decodedValue {
| Some(string) => string->String.toUpperCase->JSON.Encode.string
| None => value
}
})
JSON.stringifyAny(dict, ~replacer)
// {"foo":"BAR","hello":"WORLD","someNumber":42}
JSON.stringifyAny(() => "hello world")
// None
Expand All @@ -279,7 +358,8 @@ BigInt.fromInt(0)->JSON.stringifyAny
*/
@raises(Exn.t)
@val
external stringifyAny: 'a => option<string> = "JSON.stringify"
external stringifyAny: ('a, ~replacer: replacer=?, ~space: int=?) => option<string> =
"JSON.stringify"

/**
`stringifyAnyWithIndent(any, indentation)`
Expand Down Expand Up @@ -316,6 +396,7 @@ BigInt.fromInt(0)->JSON.stringifyAny
- Raises a TypeError if the value contains circular references.
- Raises a TypeError if the value contains `BigInt`s.
*/
@deprecated("Use `stringifyAny` with optional parameter instead")
@raises(Exn.t)
@val
external stringifyAnyWithIndent: ('a, @as(json`null`) _, int) => option<string> = "JSON.stringify"
Expand Down Expand Up @@ -361,6 +442,7 @@ BigInt.fromInt(0)->JSON.stringifyAny
- Raises a TypeError if the value contains circular references.
- Raises a TypeError if the value contains `BigInt`s.
*/
@deprecated("Use `stringifyAny` with optional parameter instead")
@raises
@val
external stringifyAnyWithReplacer: ('a, (string, t) => t) => option<string> = "JSON.stringify"
Expand Down Expand Up @@ -410,6 +492,7 @@ BigInt.fromInt(0)->JSON.stringifyAny
- Raises a TypeError if the value contains circular references.
- Raises a TypeError if the value contains `BigInt`s.
*/
@deprecated("Use `stringifyAny` with optional parameters instead")
@raises
@val
external stringifyAnyWithReplacerAndIndent: ('a, (string, t) => t, int) => option<string> =
Expand Down Expand Up @@ -447,6 +530,7 @@ BigInt.fromInt(0)->JSON.stringifyAny
- Raises a TypeError if the value contains circular references.
- Raises a TypeError if the value contains `BigInt`s.
*/
@deprecated("Use `stringifyAny` with optional parameter instead")
@raises
@val
external stringifyAnyWithFilter: ('a, array<string>) => string = "JSON.stringify"
Expand Down Expand Up @@ -486,6 +570,7 @@ BigInt.fromInt(0)->JSON.stringifyAny
- Raises a TypeError if the value contains circular references.
- Raises a TypeError if the value contains `BigInt`s.
*/
@deprecated("Use `stringifyAny` with optional parameters instead")
@raises
@val
external stringifyAnyWithFilterAndIndent: ('a, array<string>, int) => string = "JSON.stringify"
Expand Down

0 comments on commit 9a395a0

Please sign in to comment.