{"id":13881064,"url":"https://github.com/mobxjs/serializr","last_synced_at":"2025-05-14T02:07:43.891Z","repository":{"id":47337588,"uuid":"61741845","full_name":"mobxjs/serializr","owner":"mobxjs","description":"Serialize and deserialize complex object graphs to and from JSON and Javascript classes","archived":false,"fork":false,"pushed_at":"2025-04-26T08:23:51.000Z","size":1706,"stargazers_count":772,"open_issues_count":37,"forks_count":52,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-05-03T15:02:01.447Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mobxjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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,"zenodo":null},"funding":{"open_collective":"mobx"}},"created_at":"2016-06-22T18:23:51.000Z","updated_at":"2025-04-26T08:22:44.000Z","dependencies_parsed_at":"2024-01-15T02:30:06.847Z","dependency_job_id":"acb87458-a6ae-4a05-925b-850b536364a4","html_url":"https://github.com/mobxjs/serializr","commit_stats":{"total_commits":220,"total_committers":40,"mean_commits":5.5,"dds":0.7363636363636363,"last_synced_commit":"f8b9196bf6f9e47e56b0dfce2669ad7229b76c44"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mobxjs%2Fserializr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mobxjs%2Fserializr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mobxjs%2Fserializr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mobxjs%2Fserializr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mobxjs","download_url":"https://codeload.github.com/mobxjs/serializr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254052896,"owners_count":22006717,"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":[],"created_at":"2024-08-06T08:03:56.689Z","updated_at":"2025-05-14T02:07:38.879Z","avatar_url":"https://github.com/mobxjs.png","language":"TypeScript","funding_links":["https://opencollective.com/mobx"],"categories":["TypeScript","Awesome MobX"],"sub_categories":["Related projects and utilities"],"readme":"# Serializr\n\n_Serialize and deserialize complex object graphs to JSON_\n\n[![Build Status](https://travis-ci.org/mobxjs/serializr.svg?branch=master)](https://travis-ci.org/mobxjs/serializr)\n[![Coverage Status](https://coveralls.io/repos/github/mobxjs/serializr/badge.svg?branch=master)](https://coveralls.io/github/mobxjs/serializr?branch=master)\n[![Join the chat at https://gitter.im/mobxjs/serializr](https://badges.gitter.im/mobxjs/serializr.svg)](https://gitter.im/mobxjs/serializr?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![NPM](https://img.shields.io/npm/v/serializr)](https://www.npmjs.com/package/serializr)\n\n_Serializr is feature complete, and easily extendable. Since there are no active maintainers the project is frozen feature wise. Bug reports and well designed pull requests are welcome and will be addressed._\n\nWant to maintain a small open source project or having great ideas for this project? We are looking for maintainers, so [apply](https://github.com/mobxjs/serializr/issues/46)!\n\n# Api Documentation\n\nThe auto-generated documentation for the APIs is published using gitpages at:\nhttps://mobxjs.github.io/serializr/\n\n# Introduction\n\nSerializr is a utility library that helps converting json structures into complex object graphs and the other way around.\nFor a quick overview, read the [introduction blog post](https://medium.com/@mweststrate/introducing-serializr-serializing-and-deserializing-object-graphs-with-ease-8833c3fcea02#.ha9s8hkjk)\n\nFeatures:\n\n-   (De)serialize objects created with a constructor / class\n-   (De)serialize primitive values\n-   (De)serialize nested objects, maps and arrays\n-   Resolve references asynchronously (during deserialization)\n-   Supports inheritance\n-   Works on any ES5 environment (if ES3 is needed file a feature request)\n-   Convenience decorators for ESNext / Typescript\n-   Ships with typescript / flow typings\n-   Generic solution that works well with for example MobX out of the box\n\nNon-features:\n\n-   Serializr is not an ORM or data management library. It doesn't manage object instances, provided api's like fetch, search etc. If you are building such a thing though, serializr might definitely take care of the serialization part for you :-).\n-   Serializr is not a MobX specific (de)serialization mechanism, it is generic and should fit work with any type of model objects\n\n# Installation\n\nFrom npm: `npm install serializr --save`\n\nFrom CDN: \u003chttps://unpkg.com/serializr\u003e which declares the global `serializr` object.\n\n# Quick example:\n\n```javascript\nimport {\n    createModelSchema,\n    primitive,\n    reference,\n    list,\n    object,\n    identifier,\n    serialize,\n    deserialize,\n} from \"serializr\";\n\n// Example model classes\nclass User {\n    uuid = Math.floor(Math.random() * 10000);\n    displayName = \"John Doe\";\n}\n\nclass Message {\n    message = \"Test\";\n    author = null;\n    comments = [];\n}\n\nfunction fetchUserSomewhere(uuid) {\n    // Lets pretend to actually fetch a user; but not.\n    // In a real app this might be a database query\n    const user = new User();\n    user.uuid = uuid;\n    user.displayName = `John Doe ${uuid}`;\n    return user;\n}\n\nfunction findUserById(uuid, callback, context) {\n    // This is a lookup function\n    // uuid is the identifier being resolved\n    // callback is a node style callback function to be invoked with the found object (as second arg) or an error (first arg)\n    // context is an object detailing the execution context of the serializer now\n    callback(null, fetchUserSomewhere(uuid));\n}\n\n// Create model schemas\ncreateModelSchema(Message, {\n    message: primitive(),\n    author: reference(User, findUserById),\n    comments: list(object(Message)),\n});\n\ncreateModelSchema(User, {\n    uuid: identifier(),\n    displayName: primitive(),\n});\n\n// can now deserialize and serialize!\nconst message = deserialize(Message, {\n    message: \"Hello world\",\n    author: 17,\n    comments: [\n        {\n            message: \"Welcome!\",\n            author: 23,\n        },\n    ],\n});\n\nconst json = serialize(message);\n\nconsole.dir(message, { colors: true, depth: 10 });\n```\n\n## Using decorators (optional)\n\nWith decorators (TypeScript or ESNext) building model schemas is even more trivial:\n\n```javascript\nimport {\n    createModelSchema,\n    primitive,\n    reference,\n    list,\n    object,\n    identifier,\n    serialize,\n    deserialize,\n    getDefaultModelSchema,\n    serializable,\n} from \"serializr\";\n\nclass User {\n    @serializable(identifier())\n    uuid = Math.random();\n\n    @serializable\n    displayName = \"John Doe\";\n}\n\nclass Message {\n    @serializable\n    message = \"Test\";\n\n    @serializable(object(User))\n    author = null;\n\n    // Self referencing decorators work in Babel 5.x and Typescript. See below for more.\n    @serializable(list(object(Message)))\n    comments = [];\n}\n\n// You can now deserialize and serialize!\nconst message = deserialize(Message, {\n    message: \"Hello world\",\n    author: { uuid: 1, displayName: \"Alice\" },\n    comments: [\n        {\n            message: \"Welcome!\",\n            author: { uuid: 1, displayName: \"Bob\" },\n        },\n    ],\n});\n\nconsole.dir(message, { colors: true, depth: 10 });\n\n// We can call serialize without the first argument here\n//because the schema can be inferred from the decorated classes\n\nconst json = serialize(message);\n```\n\n**Decorator: Caveats**\n\nBabel 6.x does not allow decorators to self-reference during their creation, so the above code would not work for the Message class. Instead write:\n\n```javascript\nclass Message {\n    @serializable message = \"Test\";\n\n    @serializable(object(User))\n    author = null;\n\n    comments = [];\n\n    constructor() {\n        getDefaultModelSchema(Message).props[\"comments\"] = list(object(Message));\n    }\n}\n```\n\n## Enabling decorators (optional)\n\n**TypeScript**\n\nEnable the compiler option `experimentalDecorators` in `tsconfig.json` or pass it as flag `--experimentalDecorators` to the compiler.\n\n**Babel 7.x:**\n\nInstall support for decorators: `npm i --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators`. And enable it in your `.babelrc` file:\n\n```json\n{\n    \"presets\": [\"@babel/preset-env\"],\n    \"plugins\": [\n        [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n        [\"@babel/plugin-proposal-class-properties\", { \"loose\": true }]\n    ]\n}\n```\n\n**Babel 6.x:**\n\nInstall support for decorators: `npm i --save-dev babel-plugin-transform-decorators-legacy`. And enable it in your `.babelrc` file:\n\n```json\n{\n    \"presets\": [\"es2015\", \"stage-1\"],\n    \"plugins\": [\"transform-decorators-legacy\"]\n}\n```\n\n**Babel 5.x**\n\n```json\n{\n    \"stage\": 1\n}\n```\n\nProbably you have more plugins and presets in your `.babelrc` already, note that the order is important and `transform-decorators-legacy` should come as first.\n\n# Concepts\n\nThe two most important functions exposed by serializr are `serialize(modelschema?, object) -\u003e json tree` and `deserialize(modelschema, json tree) -\u003e object graph`.\nWhat are those model schemas?\n\n## ModelSchema\n\nThe driving concept behind (de)serialization is a ModelSchema.\nIt describes how model object instances can be (de)serialize to json.\n\nA simple model schema looks like this:\n\n```javascript\nconst todoSchema = {\n    factory: (context) =\u003e new Todo(),\n    extends: ModelSchema,\n    props: {\n        modelfield: PropSchema,\n    },\n};\n```\n\nThe `factory` tells how to construct new instances during deserialization.\nThe optional `extends` property denotes that this model schema inherits its props from another model schema.\nThe props section describes how individual model properties are to be (de)serialized. Their names match the model field names.\nThe combination `fieldname: true` is simply a shorthand for `fieldname: primitive()`\n\nFor convenience, model schemas can be stored on the constructor function of a class.\nThis allows you to pass in a class reference wherever a model schema is required.\nSee the examples below.\n\n## PropSchema\n\nPropSchemas contain the strategy on how individual fields should be serialized.\nIt denotes whether a field is a primitive, list, whether it needs to be aliased, refers to other model objects etc.\nPropSchemas are composable. See the API section below for the details, but these are the built-in property schemas:\n\n-   `primitive()`: Serialize a field as primitive value\n-   `identifier()`: Serialize a field as primitive value, use it as identifier when serializing references (see `reference`)\n-   `date()`: Serializes dates (as epoch number)\n-   `alias(name, propSchema)`: Serializes a field under a different name\n-   `list(propSchema)`: Serializes an array based collection\n-   `map(propSchema)`: Serializes an Map or string key based collection\n-   `mapAsArray(propSchema, keyPropertyName)`: Serializes a map to an array of elements\n-   `object(modelSchema)`: Serializes an child model element\n-   `reference(modelSchema, lookupFunction?)`: Serializes a reference to another model element\n-   `custom(serializeFunction, deserializeFunction)`: Create your own property serializer by providing two functions, one that converts modelValue to jsonValue, and one that does the inverse\n-   There is a special prop schema: `\"*\": true` that serializes all enumerable, non mentioned values as primitive\n\nIt is possible to define your own prop schemas. You can define your own propSchema by creating a function that returns an object with the following signature:\n\n```typescript\n{\n    serializer: (sourcePropertyValue: any) =\u003e jsonValue,\n    deserializer: (jsonValue: any, callback: (err, targetPropertyValue: any) =\u003e void, context?, currentPropertyValue?) =\u003e void\n}\n```\n\nFor inspiration, take a look at the source code of the existing ones on how they work, it is pretty straightforward.\n\n## Deserialization context\n\nThe context object is an advanced feature and can be used to obtain additional context-related information about the deserialization process.\n`context` is available as:\n\n1.  first argument of factory functions\n2.  third argument of the lookup callback of `ref` prop schema's (see below)\n3.  second argument of the `deserializer` of a custom propSchema\n\nWhen deserializing a model element / property, the following fields are available on the context object:\n\n-   `json`: Returns the complete current json object that is being deserialized\n-   `target`: The object currently being deserialized. This is the object that is returned from the factory function.\n-   `parentContext`: Returns the parent context of the current context. For example if a child element is being deserialized, the `context.target` refers to the current model object, and `context.parentContext.target` refers to the parent model object that owns the current model object.\n-   `args`: If custom arguments were passed to the `deserialize` / `update` function, they are available as `context.args`.\n\n## AdditionalPropArgs\n\nA PropSchema can be further parameterized using AdditionalPropArgs. Currently, they can be used to specify lifecycle functions. During deserialization they can be useful, e.g. in case you want to\n\n-   react to errors in the deserialization on a value level and retry with corrected value,\n-   remove invalid items e.g. in arrays or maps,\n-   react to changes in field names, e.g. due to schema migration (i.e. only one-directional changes that cannot be dealt with by alias operators).\n\nIt is possible to define those functions by passing them as additional property arguments to the propSchema during its creation.\n\n```javascript\nconst myHandler = {\n    beforeDeserialize: function (\n        callback,\n        jsonValue,\n        jsonParentValue,\n        propNameOrIndex,\n        context,\n        propDef\n    ) {\n        if (typeof jsonValue === \"string\") {\n            callback(null, jsonValue);\n        } else if (typeof jsonValue === \"number\") {\n            callback(null, jsonValue.toString());\n        } else {\n            callback(new Error(\"something went wrong before deserialization\"));\n        }\n    },\n    afterDeserialize: function (\n        callback,\n        error,\n        newValue,\n        jsonValue,\n        jsonParentValue,\n        propNameOrIndex,\n        context,\n        propDef\n    ) {\n        if (!error \u0026\u0026 newValue !== \"needs change\") {\n            callback(null, newValue);\n        } else if (!error \u0026\u0026 newValue === \"needs change\") {\n            callback(new Error(), \"changed value\");\n        } else {\n            callback(error);\n        }\n    },\n};\n\nclass MyData {\n    @serializable(primitive(myHandler))\n    mySimpleField;\n}\n```\n\nA more detailed example can be found in [test/typescript/ts.ts](test/typescript/ts.ts).\n\n# Inheritance\n\nWhen defining schemas or serializing Inheritance is automatically handled. When deserializing, to\ndeserialize into the right type based on a discriminator use the `@subSchema` decorator.\n\n```ts\nclass Todo {\n    @serializable\n    id: string;\n\n    @serializable\n    text: string;\n}\n\n@subSchema(\"picture\")\nclass PictureTodo extends Todo {\n    @serializable\n    pictureUrl: string;\n}\n\nconst ser = serialize(\n    Object.assign(new PictureTodo(), {\n        id: \"pic1\",\n        text: \"Lorem Ipsum\",\n        pictureUrl: \"foobar\",\n    })\n);\n// ser now holds an object like the following result\n// {\n//    id: \"pic1\",\n//    _type: \"picture\"\n//    text: \"Lorem Ipsum\",\n//    pictureUrl:\"foobar\",\n// }\nconst deser = deserialize(Todo, ser);\nconsole.log(deser instanceof PictureTodo); // true\n```\n\nSee https://mobxjs.github.io/serializr/functions/subSchema.html for more information.\n\n# Future ideas\n\n-   [ ] If MobX, optimize by leveraging createTransformer and transactions\n-   [ ] Support async serialization (future)\n-   [ ] Support ImmutableJS out of the box\n-   [ ] Make `\"*\": true` respect extends clauses\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmobxjs%2Fserializr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmobxjs%2Fserializr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmobxjs%2Fserializr/lists"}