-
Notifications
You must be signed in to change notification settings - Fork 9
add an options object to JSON.parse and JSON.stringify #5
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
Comments
I agree. I added an options object to implement JSON node parsing in my fork of JSON5 at jlyonsmith/json5. |
besides bigint (and proposed bigdecimal), are there any other compelling use-cases? if not, maybe this proposal should narrow its scope to only those? example scenario: java1 <-> nodejs-proxy <-> java2 json message: {
"docid": 123456789012345678901234567890,
"tags": ["foo", "bar"]
} is there a way for nodejs-proxy to modify the message as follows (but still preserve 64-bit docid): {
"docid": 123456789012345678901234567890,
- "tags": ["foo", "bar"]
+ "tags": ["foo", "bar", "baz"]
} i think this is the common-case scenario for this proposal to solve. |
@kaizhu256 There are absolutely compelling use cases outside of BigInt conversions. The ability to get at the JSON source information for error reporting and more open ended data type support that has some future proofing are just two good examples. |
can you give example-scenarios of other open-ended data-types? datetime and binary-data are the only other ones that come to mind, and i'm skeptical of both (isostrings and base64 are good-enough). i don't think you can add anymore datattypes to JSON w/o ruining its simplicity/idiot-proofness, which are its main selling-points. |
"Everything that can be invented has been invented" :D I'm not sure if we are quite talking about the same thing @kaizhu256 My use case is for extending the |
There may be reason to introduce an options bag to |
I would be open to extending this proposal for better supporting @kaizhu256's use case if I had confidence that it wouldn't mire things down. Unfortunately, I can only think of two feasible ways to do so, and they're both very messy.
I think it would be better to have raw JSON serialization in a separate proposal, with primary emphasis on BigInt and BigDecimal. |
I referenced (tc39/proposal-well-formed-stringify#4 (comment) ) containing my proposed solution when I opened this issue:
In other words, the two available argument formats for with any function or Array.isArray object recognized as a The two argument "options" form would still supports all the functionality available through the legacy three argument form. The standard set of recognized option properties would include a |
so with options-bag, how would actual solution to the nodejs-proxy scenario {
"docid": 1234567890, // bigint
- "tags": ["foo", "bar"]
+ "tags": ["foo", "bar", "baz"]
} look like in code? require("http").createServer(async function (req, res) {
var result;
result = await ... // read message from http-request-stream
// result = "{\"docid\":1234567890,\"tags\":[\"foo\",\"bar\"]}"
result = JSON.parse(
result,
{
reviver: function (key, val, src) {
if (key === "docid") {
return BigInt(src);
}
return val;
}
}
);
// result = {"docid":1234567890n,"tags":["foo","bar"]}
result.tags.push("baz");
// result = {"docid":1234567890n,"tags":["foo","bar","baz"]}
result = JSON.stringify(
result,
{
replacer: function (key, val) {
// stringify bigint with unique-prefix
if (typeof val === "bigint") {
return "biginit_d8safu2_" + val.toString();
}
return val;
}
}
);
// result = "{\"docid\":\"biginit_d8safu2_1234567890\",\"tags\":[\"foo\",\"bar\",\"baz\"]}"
result = result.replace((
/"biginit_d8safu2_(\d+)"/g
), "$1");
// result = "{\"docid\":1234567890,\"tags\":[\"foo\",\"bar\",\"baz\"]}"
res.end(result);
}).listen(8080); |
To start we would have to define a set of options to enable different BitInt serialization/deserialization scenarios. As a strawman, assume we have two alternatives available:
So, for this example (and based upon what I infer from above of nodejs-proxy schema) the options object could be either: require("http").createServer(async function (req, res) {
var result;
var JSONOptions = {BinIntProps: ["dicid"]};
result = await ... // read message from http-request-stream
// result = "{\"docid\":1234567890,\"tags\":[\"foo\",\"bar\"]}"
result = JSON.parse(results, JSONOptions);
// result = {"docid":1234567890n,"tags":["foo","bar"]}
result.tags.push("baz");
// result = {"docid":1234567890n,"tags":["foo","bar","baz"]}
result = JSON.stringify(result, JSONOptions);
// result = "{\"docid\":1234567890,\"tags\":[\"foo\",\"bar\",\"baz\"]}"
res.end(result);
}).listen(8080); |
@allenwb That still wouldn't work for input like {
"docid": 9007199254740992.5,
"tags": ["foo", "bar"]
} |
Instead of trying to select a scenario, it seems like the best option is to allow the user to provide a callback, invoke it with all the info it needs to make a decision, and let the user encode their scenario. (which, as i understand it, is what this proposal already achieves in combination with a reviver) |
So, what's the schema of the record being processed? In a well-formed record is the docId supposed to be a integer with an arbitrary number of digits (EG, a BitInt). Or is it an arbitrary precision decimal fraction? Or, in which case perhaps it is BigDecimal support you want. Regardless, I replied based on the assumption that the schema required docid to be an integer JSON number. If it is something else you would have to parameterize JSON.parse differently. I note that the original example in #5 (comment) parse a JSON text produces a JS object whose Also, I notice that in the original example, the reviver function accepts three arguments. The current specification for JSON.parse says a reviver only takes two arguments. Perhaps, there is TC39 proposal for adding a parameter to reviver? Or maybe the example was written assuming a non-standard implementation of Finally, my main point was to show that JSON.parse/stringify could be extended in a backwards compatible manner to enable automatic process of large integer JSON Numbers as BitInts. The specific details of the most useful ways to parameterize JSON.parse/stringify to accomplish that still needs to be worked out. |
I believe this proposal doesn’t add BigInt (or BigDecimal) support - it just provides the original source string, which leaves it up to the programmer to decide if they want BigInt, BigDecimal, or any other arbitrary data structure. |
bigint/bigdecimal are the primary (or i would argue only) motivation for this proposal. yes, i added an extra 3rd parameter to the reviver, so that bigint could be parsed. i like @allenwb's strawman for its ease-of-use, but to instead use conditional-coercion require("http").createServer(async function (req, res) {
var result;
var JSONOptions = {
// conditionally-coerce to bigint if integer and outside Number.MAX_SAFE_INTEGER
// the user (not JSON.parse) is responsible for explicit bigint coercion
BigIntConditional: true,
// secure against malicious 1-million bit bigint
BigIntMaxBits: 64
};
result = await ... // read message from http-request-stream
// result = "[\
// {\"docid\":1234, \"tags\":[]},\
// {\"docid\":12345678901234567890, \"tags\":[]}\
// ]"
result = JSON.parse(result, JSONOptions);
// result = [
// {"docid":1234, "tags":[]},
// {"docid":12345678901234567890n, "tags":[]} // conditional-coercion
// ]
result.forEach(function (elem) {
// the user (not JSON.parse) is responsible for explicit bigint coercion
elem.docid = BigInt(elem.docid);
elem.tags.push("baz");
});
// result = [
// {"docid":1234n, "tags":["baz"]},
// {"docid":12345678901234567890n, "tags":["baz"]}
// ]
result = JSON.stringify(result, JSONOptions);
// result = "[\
// {\"docid\":1234, \"tags\":[\"baz\"]},\
// {\"docid\":12345678901234567890, \"tags\":[\"baz\"]}\
// ]"
res.end(result);
}).listen(8080); schema-handling should be out-of-scope of JSON.parse if possible, to keep it idiot-proof. |
Not to put too fine a point on it, but that's this proposal.
They aren't the only motivation, and one could also imagine wanting to differentiate But this has drifted a great deal from the subject of the proposal, which as I said above should not in itself warrant changes to the argument processing of |
I want to revive a suggestion I made the last time I say a proposal to tweak ES JSON processing: tc39/proposal-well-formed-stringify#4 (comment)
There is nothing particularly sacred about the JSON/JavaScript mapping imposed by JSON.parse and JSON.stingify. They would both greatly benefit from an options object that allowed more control over the mapping (for example, enabling built-in internalization of large integer as BigInts or the features discussed in the present proposal).
I think introducing such an options object is the right place to start working on JSON processing extensions.
The text was updated successfully, but these errors were encountered: