Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use optional args for JSON.stringify and JSON.parseExn #201

Merged
merged 2 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading