Compare commits

...

2 commits

5 changed files with 212 additions and 145 deletions

View file

@ -23,7 +23,15 @@ uncompressed.
In deno:
```typescript
import { Serializer } from "https://code.thunderk.net/typescript/serializer/raw/branch/master/mod.ts";
import { Serializer } from "https://js.thunderk.net/serializer/mod.ts";
```
In browser:
```html
<script type="module">
import { Serializer } from "https://js.thunderk.net/serializer/mod.js";
</script>
```
## Use

View file

@ -1 +1,3 @@
# TODO
- Add protocol version, to handle breaking changes

View file

@ -3,7 +3,15 @@
In deno:
```typescript
import { Serializer } from "https://code.thunderk.net/typescript/serializer/raw/branch/master/mod.ts";
import { Serializer } from "https://js.thunderk.net/serializer/mod.ts";
```
In browser:
```html
<script type="module">
import { Serializer } from "https://js.thunderk.net/serializer/mod.js";
</script>
```
## Use

View file

@ -1,8 +1,9 @@
import {
describe,
expect,
it,
mock,
} from "https://code.thunderk.net/typescript/devtools/raw/1.2.2/testing.ts";
} from "https://code.thunderk.net/typescript/devtools/raw/1.3.0/testing.ts";
import { Serializer } from "./serializer.ts";
class TestSerializerObj1 {
@ -62,6 +63,7 @@ function checkReversability(
return loaded;
}
describe(Serializer, () => {
it("serializes simple objects", () => {
var obj = {
"a": 5,
@ -78,6 +80,9 @@ it("serializes simple objects", () => {
expect(result["d"].has(2)).toBe(false);
expect(result["e"].get("z")).toBe(8);
expect(result["e"].get("k")).toBeUndefined();
checkReversability(new Set(["a", new Set([1, "b"])]));
checkReversability(new Map([["a", new Map([[1, "test"]])]]));
});
it("restores objects constructed from class", () => {
@ -94,6 +99,11 @@ it("restores objects constructed from class", () => {
expect(loaded).toBeInstanceOf(TestSerializerObj1);
});
it("serializes reference to class type", () => {
let loaded = checkReversability(TestSerializerObj1);
expect(loaded).toBe(TestSerializerObj1);
});
it("stores one version of the same object", () => {
var a = new TestSerializerObj1(8);
var b = new TestSerializerObj1(8);
@ -154,7 +164,7 @@ it("ignores functions", () => {
let data = serializer.serialize({ obj: new TestSerializerObj2() });
let loaded = serializer.unserialize(data);
let expected = <any> new TestSerializerObj2();
let expected: any = new TestSerializerObj2();
expected.a = undefined;
expected.b[0] = undefined;
expect(loaded).toEqual({ obj: expected });
@ -201,3 +211,4 @@ it("uses namespace alias to protect from property mangling", () => {
expect(serializer1.unserialize(dumped1)).toEqual(data);
expect(serializer2.unserialize(dumped2)).toEqual(data);
});
});

View file

@ -41,18 +41,32 @@ export class Serializer {
// Collect objects
var objects: Object[] = [];
var stats: any = {};
function add(value: any): void {
if (objects.indexOf(value) < 0) {
objects.push(value);
}
}
crawl(obj, (value) => {
if (isObject(value)) {
var vtype = classname(value);
if (vtype != "" && this.ignored.indexOf(vtype) < 0) {
stats[vtype] = (stats[vtype] || 0) + 1;
if (objects.indexOf(value) < 0) {
objects.push(value);
}
add(value);
return value;
} else {
return STOP_CRAWLING;
}
} else if (typeof value == "function") {
const found = Object.entries(this.namespace).find(([_, cons]) =>
cons === value
);
if (found && this.ignored.indexOf(found[0]) < 0) {
// Keep references to constructors in the namespace
add(value);
}
return STOP_CRAWLING;
} else {
return value;
}
@ -63,7 +77,8 @@ export class Serializer {
var fobjects = objects.map((value) => this.encodeObject(value));
return JSON.stringify(fobjects, (key, value) => {
if (
key != "$f" && isObject(value) && !value.hasOwnProperty("$c") &&
key != "$f" && (isObject(value) || typeof value == "function") &&
!value.hasOwnProperty("$c") &&
!value.hasOwnProperty("$i")
) {
return { $i: objects.indexOf(value) };
@ -132,6 +147,10 @@ export class Serializer {
$c: ctype,
$f: Array.from(obj),
};
} else if (typeof obj == "function") {
return {
$c: obj.name,
};
} else {
return {
$c: this.namespace_rev[ctype] || ctype,
@ -149,8 +168,10 @@ export class Serializer {
return new Set(objdata.$f);
} else if (ctype == "Map") {
return new Map(objdata.$f);
} else {
} else if (objdata.$f) {
return Object.assign(this.constructObject(ctype), objdata.$f);
} else {
return this.namespace[ctype];
}
}
}
@ -159,7 +180,8 @@ export class Serializer {
* Check if the argument is an instance of a class
*/
function isObject(value: any): boolean {
return value instanceof Object && !Array.isArray(value);
return typeof value == "object" && value instanceof Object &&
!Array.isArray(value);
}
/**
@ -174,7 +196,7 @@ function crawl(
callback: (item: any) => any,
replace = false,
memo: any[] = [],
) {
): any {
if (isObject(obj)) {
if (memo.indexOf(obj) >= 0) {
return obj;
@ -183,34 +205,50 @@ function crawl(
}
}
if (obj !== undefined && obj !== null && typeof obj != "function") {
let result = callback(obj);
if (obj !== undefined && obj !== null) {
const result = callback(obj);
if (result === STOP_CRAWLING) {
return;
return undefined;
}
if (Array.isArray(obj)) {
let subresult = obj.map((value) => crawl(value, callback, replace, memo));
const subresult = obj.map((value) =>
crawl(value, callback, replace, memo)
);
if (replace) {
subresult.forEach((value, index) => {
obj[index] = value;
});
}
} else if (obj instanceof Set) {
let subresult = new Set();
for (let item of obj) {
const subresult = new Set();
for (const item of obj) {
subresult.add(crawl(item, callback, replace, memo));
}
if (replace) {
obj.clear();
for (let item of subresult) {
for (const item of subresult) {
obj.add(item);
}
}
} else if (obj instanceof Map) {
const subresult = new Map();
for (const [key, item] of obj.entries()) {
subresult.set(
crawl(key, callback, replace, memo),
crawl(item, callback, replace, memo),
);
}
if (replace) {
obj.clear();
for (const [key, item] of subresult.entries()) {
obj.set(key, item);
}
}
} else if (obj instanceof Object) {
let subresult: any = {};
for (let key in obj) {
const subresult: any = {};
for (const key in obj) {
subresult[key] = crawl(obj[key], callback, replace, memo);
}
if (replace) {
@ -228,5 +266,5 @@ function crawl(
* Get the class name of an object.
*/
function classname(obj: Object): string {
return (<any> obj.constructor).name;
return obj.constructor.name;
}