Skip to content

Commit

Permalink
Use optional args for JSON.stringify and JSON.parseExn (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
cknitt authored Apr 1, 2024
1 parent dedd5f6 commit 6725ea4
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 28 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 `JSON.parseExn` and deprecate `JSON.stringifyWithIndent`, `JSON.stringifyWithReplacer`, `JSON.parseExnWithReviver` etc. https://github.com/rescript-association/rescript-core/pull/201
- Add `Array.join` and deprecate `Array.joinWith`. https://github.com/rescript-association/rescript-core/pull/205
- 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
Expand Down
4 changes: 2 additions & 2 deletions scripts/DocTests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function prepareCompiler() {
stdio: "ignore",
cwd: compilerDir
});
var dict = JSON.parse(Fs.readFileSync(Path.join(corePath, "package.json")));
var dict = JSON.parse(Fs.readFileSync(Path.join(corePath, "package.json")), undefined);
var currentCoreVersion;
if (!Array.isArray(dict) && (dict === null || typeof dict !== "object") && typeof dict !== "number" && typeof dict !== "string" && typeof dict !== "boolean") {
throw {
Expand Down Expand Up @@ -179,7 +179,7 @@ function extractDocFromFile(file) {
"doc",
file
]);
return Tools_Docgen.decodeFromJson(JSON.parse(spawn.stdout.toString()));
return Tools_Docgen.decodeFromJson(JSON.parse(spawn.stdout.toString(), undefined));
}

function getExamples(param) {
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
101 changes: 90 additions & 11 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,25 @@ try {
} catch {
| Exn.Error(_) => Console.log("error")
}
let reviver = (_, value: JSON.t) =>
switch value {
| 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 +62,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 All @@ -50,15 +73,12 @@ It returns a JSON type.
## Examples
```rescript
let reviver = (_, value) => {
let valueType = JSON.Classify.classify(value)
switch valueType {
let reviver = (_, value: JSON.t) =>
switch value {
| String(string) => string->String.toUpperCase->JSON.Encode.string
| Number(number) => (number *. 2.0)->JSON.Encode.float
| _ => value
}
}
let jsonString = `{"hello":"world","someNumber":21}`
Expand All @@ -77,14 +97,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 +121,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 = JSON.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 +171,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 +204,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 +241,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 +265,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 +292,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 +316,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 = JSON.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 +352,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 +390,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 +436,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 +486,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 +524,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 +564,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
2 changes: 1 addition & 1 deletion test/TempTests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ console.info("JSON");

console.info("---");

var json = JSON.parse("{\"foo\": \"bar\"}");
var json = JSON.parse("{\"foo\": \"bar\"}", undefined);

var json$1 = Core__JSON.Classify.classify(json);

Expand Down
2 changes: 1 addition & 1 deletion test/Test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function print(value) {
if (match === "object" || match === "bigint") {
return Util.inspect(value);
} else if (match === "string") {
return Core__Option.getExn(JSON.stringify(value));
return Core__Option.getExn(JSON.stringify(value, undefined, undefined));
} else {
return String(value);
}
Expand Down

0 comments on commit 6725ea4

Please sign in to comment.