Skip to content

Commit acc5c06

Browse files
author
Dmitry Dutikov
committed
Readme updated
1 parent 439db6e commit acc5c06

File tree

2 files changed

+172
-3
lines changed

2 files changed

+172
-3
lines changed

README.md

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,165 @@
1-
# JSON22
2-
JSON superset with an ability to serialize/deserialize classes
1+
# JSON22 - JSON with types
2+
The JSON22 is a superset of [JSON](https://tools.ietf.org/html/rfc7159) with an ability to serialize/deserialize classes and extended support for number variables
3+
4+
## TL;DR
5+
### To there ...
6+
```javascript
7+
const value = {
8+
name: "Femistoclus",
9+
amount: 3873133n,
10+
debt: NaN,
11+
date: new Date('2022-01-01'),
12+
};
13+
const string = JSON22.stringify(value);
14+
console.log(string);
15+
// =>
16+
// {
17+
// "name": "Femistoclus"
18+
// "amount": 3873133n,
19+
// "debt": NaN,
20+
// "date": Date(1641513600000),
21+
// }
22+
```
23+
24+
### ... and back
25+
```javascript
26+
const string = `{
27+
"name": "Femistoclus"
28+
"amount": 3873133n,
29+
"debt": NaN,
30+
"date": Date(1641513600000),
31+
}`;
32+
const value = JSON22.parse(string);
33+
console.log(typeof value.date, value.date.constructor.name); // => object Date
34+
console.log(typeof value.amount); // => bigint
35+
console.log(typeof value.debt, isNaN(value.debt)); // => number true
36+
```
37+
## Motivation
38+
JSON format is good enough for everyday usage. There is some libraries trying to introduce syntax to make JSON closer
39+
to modern JavaScript, some libraries trying to introduce functions serialization. All that is not important and do not
40+
required for everyday usage. However, there is one think annoing me always - date values.
41+
42+
We are serializing dates a lot and each time we parse it back we get a string. As a result we have to deal with the Date
43+
constructor manually each time. Even we are no need date as object we will have to format it out to make more user friendly.
44+
Otherwords we should care about dates additionally.
45+
46+
But I'm lazy developer, I'll do everything to get rid of any additional careness.
47+
48+
## API
49+
```typescript
50+
class JSON22 {
51+
static parse(text: string, options?: Json22ParseOptions): any;
52+
static stringify(value: any, options?: Json22StringifyOptions): string;
53+
}
54+
55+
interface Json22ParseOptions {
56+
context?: Record<string, { new (...args: any) }>; // default { 'Date': Date }
57+
// To be extended
58+
}
59+
60+
interface Json22StringifyOptions {
61+
// To be extended
62+
}
63+
```
64+
65+
## JSON Extensions
66+
67+
### Numbers
68+
69+
With JSON22 you can use `NaN`, `Infinity`, `-Infinity` values. It mean also this values will be stringified well
70+
in case it nested at an array or an object.
71+
```javascript
72+
JSON.stringify([42, NaN, Infinity, -Infinity]); // => [42, null, null, null]
73+
JSON22.stringify([42, NaN, Infinity, -Infinity]); // => [42, NaN, Infinity, -Infinity]
74+
```
75+
```javascript
76+
JSON.stringify({ nan: NaN }); // => { "nan": null }
77+
JSON22.stringify({ nan: NaN }); // => { "nan": NaN }
78+
```
79+
80+
### BigInt
81+
JSON22 introduce support for BigInt values
82+
```javascript
83+
JSON.stringify({ bigint: 123n }); // => Uncaught TypeError: Do not know how to serialize a BigInt
84+
JSON22.stringify({ bigint: 123n }); // => { "bigint": 123n }
85+
JSON22.parse('{ "bigint": 123n }'); // => { bigint: 123n }
86+
```
87+
88+
### Trailing commas
89+
It was not planned, but parser implementation work well with trailing commas.
90+
We are not going to complicate parser code to avoid it. It looks useful.
91+
92+
```javascript
93+
JSON.parse('[1, 2, 3, ]'); // => Uncaught SyntaxError: Unexpected token ] in JSON at position 9
94+
JSON22.parse('[1, 2, 3, ]'); // => [1, 2, 3]
95+
```
96+
```javascript
97+
JSON.parse('{ "a": 1, "b": 2, "c": 3, }'); // => Uncaught SyntaxError: Unexpected token } in JSON at position 26
98+
JSON22.parse('{ "a": 1, "b": 2, "c": 3, }'); // => { a: 1, b:2, c:3 }
99+
```
100+
### Typed values
101+
This is the most significant addition. It's allow you to serialize and deserialize any typed value.
102+
Out of the box it works well with date values.
103+
```javascript
104+
const date = new Date('2022-01-07');
105+
JSON.stringify(date); // => '"2022-01-07T00:00:00.000Z"'
106+
JSON22.stringify(date); // => Date(1641513600000)
107+
```
108+
```javascript
109+
const date = JSON22.parse('Date(1641513600000)');
110+
console.log(typeof date, date instanceof Date); // => object true
111+
```
112+
This behavior is based on the `valueOf` method which is defined at the Object class.
113+
In case JSON22 find the `valueOf` method return a value which is not equal of the object itself then it will produce
114+
constructor literal. The `valueOf` of the Date class return numeric date representation.
115+
It you'll call Date constructor with that value date will be sort of 'restored'.
116+
117+
#### Custom valueOf implementation
118+
To match this behavior you may implement you own `valueOf` method at you custom class.
119+
120+
Let's define a model class for demonstration
121+
```javascript
122+
class TypedModel {
123+
a;
124+
b;
125+
constructor(data) {
126+
this.a = data.a;
127+
this.b = data.b;
128+
}
129+
valueOf() {
130+
return { a: this.a, b: this.b };
131+
}
132+
}
133+
```
134+
That sort of classes will be serialised as typed objects
135+
```javascript
136+
const value = new TypedModel({ a: 1, b: 2 });
137+
JSON22.stringify(value); // => TypedModel({ "a": 1, "b": 1 })
138+
```
139+
The `valueOf` methods may return any serializable values, even typed objects
140+
```javascript
141+
const value = new TypedModel({ a: 1, b: new Date('2022-01-07') });
142+
JSON22.stringify(value); // => TypedModel({ "a": 1, "b": Date(1641513600000) })
143+
```
144+
#### Parsing context
145+
Typically, serialization and deserialization are processes separated by different environments.
146+
Like serialization at a backend and deserialization at a frontend and vice versa.
147+
So `TypedModel` we defined above should be shared between environments.
148+
Also `JSON22` parser should have a link to this class. In theory, we can push all such classes to a global scope.
149+
It is easy, however, it is not the best solution. It will produce global scope pollution, may cause naming conflicts,
150+
and it is not safe to allow parser to call any constructor from a global scope. That is why you should always pass
151+
deserialization context to parser.
152+
```javascript
153+
const string = 'TypedModel({ "a": 1, "b": Date(1641513600000) })';
154+
JSON22.parse(string); // => Error: Constructor TypedModel not defined in the context
155+
156+
const context = { 'TypedModel': TypedModel };
157+
const value = JSON22.parse(string, { context });
158+
console.log(value instanceof TypedModel); // => true
159+
```
160+
161+
#### The `valueOf` method priority
162+
The `JSON22` support for `toJSON` method of an object as well as `JSON`. In some cases an object may have both `valueOf`
163+
and `toJSON` methods. Typical example is the Date class. The `JSON22` at first is a solution to serialize/deserialize
164+
date values, so `valueOf` have higher priority then `toJSON`. This is also true for any object implementing `valueOf`
165+
and `toJSON` both.

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
},
1010
"keywords": [
1111
"json",
12-
"serialization"
12+
"serialization",
13+
"NaN",
14+
"Infinity",
15+
"BigInt",
16+
"types",
17+
"valueOf",
18+
"toJSON"
1319
],
1420
"author": "Dmitry Dutikov",
1521
"--license": "ISC",

0 commit comments

Comments
 (0)