npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@endcrystal/jsonex

v0.5.1

Published

JSON扩展

Readme

@endcrystal/jsonex JSON扩展

与原生JSON的区别就是
JSONEx将类型信息一并序列化
这在模块化之前较易实现,只需保存类名,并在反序列化时访问这些全局的类即可
而在模块化之后则不能这样实现,因为不存在全局的类
JSONEx的本质就是将被@Serializable()修饰的类放到一个结构中存储起来,并在反序列化时在这个结构中读取而已
除此之外JSONEx还包含Map Set类型变量的序列化
灵感来源来自RPG Maker MZ中实现的JSONEx,但是在模块化之后失效

用前须知

请确保tsconfig.json中已开启以下项目:

{
    "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
    }
}

使用用例:

也可查看src/test/Json.test.ts

使用装饰器

可以使用装饰器使类可被类型序列化和反序列化
下面是一个使用用例:

测试类定义:

import {SuperClass} from "./SuperClass";
import {Serializable, Transient} from "../index";
import {SubClass1} from "./SubClass1";
import {SubClass2} from "./SubClass2";
import {TypeClass} from "./TypeClass";

// 必须使用序列化装饰器标记类,否则类将被反序列化为Object:
// 修饰器的参数没有任何作用,也可以不提供,参数只是为了提醒不要使用类型导入子类型
// 例如没有提供SubClass2
@Serializable(() => SuperClass, () => SubClass1, () => TypeClass)
export class RootClass {
    num: number = 2;

    // 被@Transient修饰的属性,不会被序列化
    @Transient
    str: string = "临时变量";

    boolArr: boolean[] = [];

    b: SuperClass = new SuperClass(2);

    obj = {t: 1, t2: ""};

    bObj: SuperClass = new SuperClass(3);

    // 此时可能会使用类型导入导入TypeClass,这样的话会导致反序列化失败,可以使用@Serializable可选的参数提醒
    typeClass: TypeClass | null = null;

    // 即使是子类也可以被正常反序列化,请确保子类也被@Serializable修饰
    c: SuperClass = new SubClass1(3);

    d: SuperClass = new SubClass2(4);

    bArr: SuperClass[] = [];

    set: Set<SuperClass> = new Set<SuperClass>();

    map: Map<string, SuperClass> = new Map<string, SuperClass>();

    map2: Map<SuperClass, number> = new Map();

    map3: Map<SuperClass, SuperClass> = new Map();

    map4: Map<string, number> = new Map();

    getNum(): number {
        return this.num;
    }
}
import Serializable from "../Serializable";
import Transient from "../decorator/Transient";

@Serializable()
export class SuperClass {
    @Transient
    bool = true;

    constructor(public n: number) {
    }

    getN() {
        return this.n;
    }
}
import Serializable from "../Serializable";
import {SuperClass} from "./SuperClass";
import StringifyBefore from "../decorator/StringifyBefore";
import ParseAfter from "../decorator/ParseAfter";

@Serializable()
export class SubClass1 extends SuperClass {
    message: string = "SubClass1";

    // 被@StringifyBefore装饰的方法会在序列化之前运行
    @StringifyBefore
    stringifyBefore() {
        this.message += "/stringifyBefore";
        console.log("stringifyBefore");
    }

    // 被@ParseAfter装饰的方法会在反序列化之后运行
    @ParseAfter
    parseAfter() {
        this.message += "/parseAfter";
        console.log("parseAfter");
    }
}
import Serializable from "../Serializable";
import {SuperClass} from "./SuperClass";

@Serializable()
export class SubClass2 extends SuperClass {
    message: string = "SubClass2";
}
import Serializable from "../Serializable";

@Serializable()
export class TypeClass {
    str = "TypeClass";

    constructor(public n: number) {
    }

    getN() {
        return this.n;
    }
}

序列化与反序列化:

import {JSONExImpl} from "../JSONExImpl";
import {RootClass} from "./RootClass";
import {SuperClass} from "./SuperClass";
import {SubClass1} from "./SubClass1";
import {SubClass2} from "./SubClass2";
import {TypeClass} from "./TypeClass";

it('json', function () {
    let a = new RootClass();
    a.num = 10;

    a.bArr.push(new SuperClass(1));
    a.bArr.push(new SuperClass(3));
    a.bArr.push(new SubClass1(3));
    a.bArr.push(new SubClass2(4));

    a.boolArr.push(true);
    a.boolArr.push(false);

    a.set.add(new SuperClass(11));
    a.set.add(new SuperClass(11));
    a.set.add(new SuperClass(12));
    a.set.add(new SubClass1(13));

    a.map.set("21", new SuperClass(21));
    a.map.set("22", new SuperClass(22));
    a.map.set("sub1", new SubClass1(23));
    a.map.set("sub2", new SubClass2(24));

    a.map2.set(new SuperClass(23), 23);
    a.map2.set(new SuperClass(23), 24);

    a.map3.set(new SuperClass(25), new SuperClass(25));
    a.map3.set(new SuperClass(26), new SuperClass(27));

    a.map4.set("1", 1);
    a.map4.set("2", 3);

    a.typeClass = new TypeClass(6)

    let s = JSONExImpl.stringify(a);
    console.log(s);

    let a1 = JSONExImpl.parse(s, RootClass);
    expect(<unknown>a1 instanceof RootClass).toBeTruthy();
    expect(<unknown>a1.b instanceof SuperClass).toBeTruthy();
    expect(a1.c instanceof SubClass1).toBeTruthy();
    expect(a1.d instanceof SubClass2).toBeTruthy();
    expect(a1.bArr[0] instanceof SuperClass).toBeTruthy();
    expect(a1.bArr[1] instanceof SuperClass).toBeTruthy();
    expect(a1.bArr[2] instanceof SubClass1).toBeTruthy();
    expect(a1.bArr[3] instanceof SubClass2).toBeTruthy();
    expect(a1.map.get("21") instanceof SuperClass).toBeTruthy();
    expect(a1.map.get("22") instanceof SuperClass).toBeTruthy();
    expect(a1.map.get("sub1") instanceof SubClass1).toBeTruthy();
    expect(a1.map.get("sub2") instanceof SubClass2).toBeTruthy();
    expect(a1.typeClass instanceof TypeClass).toBeTruthy();
    expect(a1.set instanceof Set).toBeTruthy();
    expect(a1.set.size).eq(4);
    debugger;
});

直接注册

如果希望一些库的类支持类型序列化,可以使用直接调用装饰器的方法:

import Serializable from "../Serializable";
import LinkedList from "libName";

Serializable()(LinkedList);

自定义序列化器

一些类因为特殊的机制无法自动的被带类型序列化
需要对其进行一些转换之后再进入序列化过程
例如Map和Set
此时可以注册自定义序列化器
已自动注册Map和Set的自定义序列化器
可参考其注册方法:

registerCustomSerializer({
        constructor: Map,
        replacer: map => Array.from(map.entries()),
        reviver: json => new Map(json)
    }
);

registerCustomSerializer({
    constructor: Set,
    replacer: before => Array.from(before.values()),
    reviver: later => new Set(later)
});