{"id":15089164,"url":"https://github.com/elemental-mind/serdestrium","last_synced_at":"2026-01-05T03:35:42.462Z","repository":{"id":250071367,"uuid":"828294939","full_name":"elemental-mind/serdestrium","owner":"elemental-mind","description":"Streaming Serialization/Deserialization with ES6/Typescript class support","archived":false,"fork":false,"pushed_at":"2024-07-27T23:14:55.000Z","size":115,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-26T10:08:28.304Z","etag":null,"topics":["browser","es6-classes","json","nodejs","oboejs","serialization-deserialization","serialization-library","streaming","typescript","xml","yaml"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elemental-mind.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-13T17:38:47.000Z","updated_at":"2024-07-26T19:00:11.000Z","dependencies_parsed_at":"2024-09-29T23:01:02.310Z","dependency_job_id":null,"html_url":"https://github.com/elemental-mind/serdestrium","commit_stats":{"total_commits":28,"total_committers":1,"mean_commits":28.0,"dds":0.0,"last_synced_commit":"2bf800b4da4c5629d4da9653945ac27a90a321d5"},"previous_names":["elemental-mind/serdestrium"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elemental-mind%2Fserdestrium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elemental-mind%2Fserdestrium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elemental-mind%2Fserdestrium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elemental-mind%2Fserdestrium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elemental-mind","download_url":"https://codeload.github.com/elemental-mind/serdestrium/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244813876,"owners_count":20514685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","es6-classes","json","nodejs","oboejs","serialization-deserialization","serialization-library","streaming","typescript","xml","yaml"],"created_at":"2024-09-25T08:41:32.619Z","updated_at":"2026-01-05T03:35:42.420Z","avatar_url":"https://github.com/elemental-mind.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Serdestrium\n\n*Ser*ialization, *De*serialization through *Str*eams with native support for ES6/TypeScript classes everywhere (browser, node etc.). Serdestrium lets you quickly and efficiently serialize and deserialize even larger-than memory files to and from JSON, YAML and XML.\n\n## Main features at a glance\n\nSerdestrium natively supports:\n- Synchronous and asynchronous streaming for serialization and deserialization\n- TypeScript/ES6 classes\n- Circular references\n- Built-in-types:\n    - Map\n    - Set\n    - Date\n    - TypedArray, ArrayBuffer\n    - Symbol\n    - Constants: undefined, null, NaN, Infinity\n- Class-level hooks (instead of decorators) for customizing Serialization and Deserialization of class instances, if needed\n- Multiple file-formats:\n    - JSON\n    - YAML (soon)\n    - XML (soon)\n\n- Wide JS environments support (browser, node, deno, bun, cf-workers etc.)\n- Tree-shakeable\n- Dependency-free\n\n## Installation\n\nTo install Serdestrium, run the following command:\n\n```\nnpm install serdestrium\n```\n\nThis will install the latest version of Serdestrium and add it to your project's dependencies.\n\n## API\n\n### Serializing\n\nFor synchronous serialization you can use the `serialize` member present on the Serializer classes:\n\n```Typescript\nimport { JSONSerializer } from 'serdestrium';\n\n// Create a new JSONSerializer instance\nconst serializer = new JSONSerializer();\n\n// Serialize a simple object\nconst simpleObject = { name: \"John\", age: 30 };\n// Output: {\"name\":\"John\",\"age\":30}\nconst serializedSimple = serializer.serialize(simpleObject); \n```\n\nIf you want to serialize an object asynchronously, you can use the generator returned from the `stream` method of the Serializer classes:\n\n```Typescript\nimport { JSONSerializer } from 'serdestrium';\n\n// Create a new JSONSerializer instance\nconst serializer = new JSONSerializer();\n\n// Serialize a simple object\nconst simpleObject = { name: \"John\", age: 30 };\n\nfor(const chunk of JSONSerializer.stream(simpleObject))\n    // Outputs in order: '{', '\"', 'name', ':', '\"', 'John', '\"', ',', '\"', 'age', ':', ' 30', '}'\n    await saveChunk(chunk);\n```\nYou can see that the chunks are really fine grained. It is recommended to use your own solution to concatenate these mini-chunks together before passing them on to be saved in files or to be sent over the network.\n\n\u003e Note: The serializer is stateful as it assigns objects ids and and uses them in references. You can theoretically use it multiple times, but your references will be mixed up if you do not deserialize in the same order in which you serialized.\n\n### Deserializing\n\nFor synchronous deserialization when you have the full string already in memory you can use the `parse` member present on the Interpreter classes:\n\n```Typescript\nimport { JSONInterpreter } from 'serdestrium';\n\n// Create a new JSONSerializer instance\nconst interpreter = new JSONInterpreter();\n\n// Serialize a simple object\nconst parsed = interpreter.parse('{\"name\":\"John\",\"age\":30}'); \n```\n\nIf you want to parse an object asynchronously straight from a file stream for example, you can use the generator returned from the `stream` method of the Serializer classes:\n\n```Typescript\nimport { JSONInterpreter } from 'serdestrium';\n\n// Create a new JSONSerializer instance\nconst interpreter = new JSONInterpreter();\n\n//The stream should be an async generator that you would normally use like this:\n// for await (const chunk of readFileTextAsync(\"fileName\", utf8)) ...\nconst parsed = await interpreter.parseStream(fileOrNetworkStreamAsyncIterable); \n```\nThere are no limits on chunk size. \n\nThe parser itself can be halted and resumed any time as it's fully generator based. To feed data differently to the parser, you need to call the `advance(chunk)` method repeatedly. Best take a look at the `parseStream` method in the source to see what needs to be done.\n\n\u003e Note: Just as the serializer, the deserializer is also stateful. Multiple calls to it may lead to unintended outcomes.\n\n## Serialization customization\n\nBy default all enumerable properties of an object or class instance are going to be serialized.\n\n### Serialization hooks\n\nSerdestrium provides serialization hooks that allow you to customize the serialization and deserialization process for your classes. These hooks are defined in the `ICustomSerialization` interface and can be implemented by your classes to control how they are serialized and deserialized.\n\nThe available serialization hooks are:\n\n- `onSerialization(dataObject: any): void`\n   - This hook is called before a class instance is serialized.\n   - Populate the given `dataObject` with the properties you want serialized.\n   - The then modified `dataObject` will be serialized. Return values will be ignored.\n   - The `onPostSerialization` hook will be ignored.\n\n- `onPostSerialization(dataObject: any): void`\n   - This hook is called after a class is prepared for serialization (it went thorugh the normal framework conversion process of instance =\u003e POJO), but before the POJO is serialized.\n   - You can use this hook to modify the pre-populated `dataObject` before its finally serialized.\n\n- `onDeserialization(dataObject: any): void | any`\n   - This hook is called after the instance is created, but before its properties are automatically assigned.\n   - You need to do assignments to the properties of the instance yourself.\n   - The `onPostDeserialization` hook will be ignored.\n\n- `onPostDeserialization(): void | any`\n   - This hook is called after an object has been deserialized and its properties were assigned.\n   - You can use it to perform any post-deserialization tasks or modifications.\n\n### Hooks usage\n\nTo use these hooks, implement the necessary `ICustomSerialization` interface members in your class:\n\n```typescript\nimport { ICustomSerialization } from 'serdestrium';\n\nclass MyCustomClass implements ICustomSerialization \n{\n    #privateData: string;\n    public notToBeSerialized?: number;\n\n    constructor(public name: string, privateData: string) \n    {\n        this.#privateData = privateData;\n    }\n\n    onPostSerialization(dataObject: any): void {\n        // dataObject only contains \"name\" as it's the only enumerable property.\n        // Make sure private property is also serialized\n        dataObject.private = this.#privateData;\n        delete dataObject.notToBeSerialized;\n    }\n\n    onDeserialization(dataObject: any): void \n    {\n        this.name = dataObject.name;\n        this.#privateData = dataOobject.private;\n    }\n}\n```\n\n## Environment configuration\n\nEach Serializer and Deserializer takes in an optional `IEnvironment` specifiying known classes, symbols and objects.\n\n\u003e Note: If you want to deserialize classes, you **must** to provide an environment configuration with a `knownClasses` map as there is no way to auto-discover classes.\n\n### Classes\nOn Serialization, by default instances get assigned a type-string that equals their prototype's `constructor.name` property:\n```\nclass CustomClass {};\n\nserializer.serialize(new CustomClass()) // returns '{\"[Type]\": \"CustomClass\"}'\n```\nThis may lead to naming conflicts if you have multiple classes of the same name in different modules or in different scopes. To avoid this you can assign classes custom type-strings:\n\n```Typescript\n//moduleA.ts\nexport class CustomClass {};\n\n//moduleB.ts\nexport class CustomClass {};\n\n//moduleSerialization.ts\nimport { JSONSerializer } from 'serdestrium';\nimport { CustomClass as ModuleACustomClass } from './moduleA.js';\nimport { CustomClass as ModuleBCustomClass } from './moduleB.js';\n\nconst serializer = new JSONSerializer({\n    knownClasses: new Map([\n        [ModuleACustomClass, 'ModuleACustomClass'],\n        [ModuleBCustomClass, 'ModuleBCustomClass']\n    ])\n});\n\nserializer.serialize([new ModuleACustomClass(), new ModuleBCustomClass()]) \n// returns '[{\"[Type]\": \"ModuleACustomClass\"}, {\"[Type]\": \"ModuleBCustomClass\"}]'\n```\n\nFor deserialization you need to provide a map of known classes, that maps type-strings to classes in your deserialization environment:\n\n```Typescript\nimport { JSONInterpreter } from 'serdestrium';\n\nclass CustomClass {};\nclass AmbiguousClass {};\n\nconst knownClasses = [CustomClass];\n//Standard mapping from classes to their names.\nconst knownClassesMap = new Map(knownClasses.map(clss =\u003e [clss, clss.name]));\n//Custom names for ambiguous classes if needed.\nknownClassesMap.set(AmbiguousClass, \"UnAmbiguousTypeString\");\n\nconst parser = new JSONInterpreter({knownClasses: knownClassesMap});\n\nparser.parse('[{\"[Type]\": \"CustomClass\"}, {\"[Type]\": \"UnAmbiguousTypeString\"}]') \n```\n\n### References \u0026 Objects\n\nDuring serialization, objects are assigned reference names automatically. By default, these references are named using the pattern `[ref: ~n]`, with a literal `~` and `n` being an incrementing alphanumeric string. This id is incremented for every encountered object to be serialized, regardless of whether it's referenced later or not.\n\nYou can also provide known objects to the serializer using the `knownObjects` property of the environment configuration.\nUse the `knownObjects` property if you:\n\n1. do not want to serialize an object or\n2. deserialize an object to a certain object that is present in the deserialization environment\n3. are working with a system where object identity needs to be preserved across serialization boundaries.\n\n\u003eNote: Objects in the `knownObjects` map will not be serialized! You can **not** use this property to give certain object's references descriptive names, but still expect these objets to be serialized.\n\n```typescript\nconst ambientObject = { name: \"Ambient\" };\nconst serializer = new JSONSerializer({\n    knownObjects: new Map([[ambientObject, \"ambientObj\"]])\n});\n\nconst result = serializer.serialize(\n    //This object is the first the serializer will encounter and gets automatic id of ~1\n    {\n        local:\n            //This is the 2nd object that the serializer will encounter and get automatic id of ~2 \n            { name: \"Local\" },\n        ambient: ambientObject,\n        localRef: this.local,\n    }\n);\n\nconsole.log(result);\n// Output: {\"local\":{\"name\":\"Local\"},\"ambient\":\"[ref: ambientObj]\",\"localRef\":\"[ref: ~2]\"}\n```\nIn this example, the `ambientObject` is given the reference name \"ambientObj\", while the local object is automatically assigned a reference name like `~2`.\n\n### Symbols\nSymbols are serialized using the `[sym: symbolName]` format. By default, symbols are not serialized and will throw an error if encountered. To enable symbol serialization, you need to provide a map of known symbols to the serializer and interpreter.\n\nFor serialization:\n```typescript \nconst symbol = Symbol(\"test\");\nconst serializer = new JSONSerializer({ knownSymbols: new Map([[symbol, \"test\"]]) });\nconst result = serializer.serialize(symbol);\n// result will be '\"[sym: test]\"'\n```\n\nFor deserialization:\n\n```typescript\nconst testSymbol = Symbol(\"test\");\nconst interpreter = new JSONInterpreter({ knownSymbols: new Map([[testSymbol, \"test\"]]) });\nconst result = interpreter.parse('\"[sym: test]\"');\n// result will be the testSymbol\n```\nYou can of course also use symbols as object keys:\n```typescript\nconst symbol = Symbol(\"SymProp\");\nconst serializer = new JSONSerializer({ knownSymbols: new Map([[symbol, \"SymProp\"]]) });\nconst obj = {\n    [symbol]: \"value\"\n};\nconst result = serializer.serialize(obj);\n// result will be '{\"[sym: SymProp]\":\"value\"}'\n```\nWhen deserializing, the interpreter will reconstruct the object with the symbol as a key:\n```typescript\nconst testSymbol = Symbol(\"SymProp\");\nconst interpreter = new JSONInterpreter({ knownSymbols: new Map([[testSymbol, \"SymProp\"]]) });\nconst result = interpreter.parse('{\"[sym: SymProp]\":\"value\"}');\n// result will be an object with testSymbol as a key\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felemental-mind%2Fserdestrium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felemental-mind%2Fserdestrium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felemental-mind%2Fserdestrium/lists"}