{"id":27288161,"url":"https://github.com/nestorrente/true-json","last_synced_at":"2026-05-09T10:09:59.714Z","repository":{"id":39797594,"uuid":"364715496","full_name":"nestorrente/true-json","owner":"nestorrente","description":"Respectful JSON serialization \u0026 deserialization for JavaScript","archived":false,"fork":false,"pushed_at":"2024-08-19T19:23:33.000Z","size":909,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-09T02:46:33.966Z","etag":null,"topics":["converter","javascript","json","parser","serialization","type","typed","typescript"],"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/nestorrente.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2021-05-05T21:57:30.000Z","updated_at":"2022-04-22T06:38:15.000Z","dependencies_parsed_at":"2024-08-21T01:07:27.903Z","dependency_job_id":null,"html_url":"https://github.com/nestorrente/true-json","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nestorrente%2Ftrue-json","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nestorrente%2Ftrue-json/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nestorrente%2Ftrue-json/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nestorrente%2Ftrue-json/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nestorrente","download_url":"https://codeload.github.com/nestorrente/true-json/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248475357,"owners_count":21110072,"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":["converter","javascript","json","parser","serialization","type","typed","typescript"],"created_at":"2025-04-11T20:28:15.854Z","updated_at":"2026-05-09T10:09:54.679Z","avatar_url":"https://github.com/nestorrente.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TrueJSON\n\nRespectful JSON serialization \u0026 deserialization for JavaScript\n\n[![License](https://img.shields.io/npm/l/make-coverage-badge.svg)](https://opensource.org/licenses/MIT)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)\n\n[comment]: \u003c\u003e ([![npm]\u0026#40;https://img.shields.io/npm/dw/true-json.svg\u0026#41;]\u0026#40;https://www.npmjs.com/package/true-json\u0026#41;)\n\n## Table of contents\n\n* [What's TrueJSON?](#whats-truejson)\n    + [What's wrong with `JSON.stringify()` and `JSON.parse()`?](#whats-wrong-with-jsonstringify-and-jsonparse)\n    + [TrueJSON to the rescue](#truejson-to-the-rescue)\n    + [Features](#features)\n* [Installation](#installation)\n    + [Using NPM (module)](#using-npm-module)\n    + [Using `\u003cscript\u003e` tag (standalone)](#using-script-tag-standalone)\n* [Basic usage](#basic-usage)\n    + [Using `import` (module)](#using-import-module)\n    + [Using `TrueJSON` object (standalone)](#using-truejson-object-standalone)\n* [Using JSON5 and other JSON alternatives](#using-json5-or-other-json-alternatives)\n* [Built-in adapters](#built-in-adapters)\n    + [isoDate()](#isodate)\n    + [dateTimestamp()](#datetimestamp)\n    + [array(elementAdapter)](#arrayelementadapter)\n    + [set(\\[elementAdapter\\])](#setelementadapter)\n    + [record(valueAdapter)](#recordvalueadapter)\n    + [mapAsEntries(\\[config\\])](#mapasentriesconfig)\n    + [mapAsRecord(\\[config\\])](#mapasrecordconfig)\n    + [object(propertyAdapters\\[, config\\])](#objectpropertyadapters-config)\n    + [byKey(keyValuePairs)](#bykeykeyvaluepairs)\n    + [byKeyLenient(keyValuePairs\\[, fallbackKey\\])](#bykeylenientkeyvaluepairs-fallbackkey)\n    + [Identity adapters](#identity-adapters)\n        + [identity(\\[validator\\])](#identityvalidator)\n        + [stringIdentity()](#stringidentity)\n        + [numberIdentity()](#numberidentity)\n        + [integerIdentity()](#integeridentity)\n        + [booleanIdentity()](#booleanidentity)\n    + [Handling nullish values](#handling-nullish-values)\n        + [nullishAware(adapter)](#nullishawareadapter)\n        + [nullAware(adapter)](#nullawareadapter)\n        + [undefinedAware(adapter)](#undefinedawareadapter)\n* [Writing your own adapter](#writing-your-own-adapter)\n* [Contributing](#contributing)\n\n## What's TrueJSON?\n\nTrueJSON is a JSON serialization and deserialization library for JavaScript and TypeScript. It helps with the serialization and\ndeserialization of complex types such as `Date`, `Set`, `Map`, and many others.\n\n### What's wrong with `JSON.stringify()` and `JSON.parse()`?\n\nImagine the following JavaScript object:\n\n```javascript\nconst originalObject = {\n    date: new Date(),\n    set: new Set([1, 2, 3])\n};\n\nconst serializedObject = JSON.stringify(originalObject);\n\nconst deserializedObject = JSON.parse(serializedObject);\n```\n\nIf you check the value of the `serializedObject` variable, you'll see the following JSON structure:\n\n```json\n{\n    \"date\": \"2021-05-08T06:39:29.215Z\",\n    \"set\": {}\n}\n```\n\nAs you can see, your set elements haven't been serialized as you would expect. Moreover, if you check the value of the\n`deserializedObject` variable, you'll see the following object:\n\n![Deserialized object using native JSON (Google Chrome console)](docs/img/deserialized-object-native.png \"Deserialized object using native JSON (Google Chrome console)\")\n\nNow the `date` property is a `string`, and the `set` property is an empty object, which probably isn't the desired behaviour.\n\n### TrueJSON to the rescue\n\nUsing TrueJSON, you can create a `JsonConverter` that knows how to serialize and deserialize your object without loosing any\ninformation. This can be done by using _JSON adapters_, which are components that know how to adapt some data types to\n_jsonable_ values. Let's see an example:\n\n```javascript\nimport { JsonConverter, JsonAdapters } from 'true-json';\n\n// Create a converter using adapters to describe your object's type\nconst customJsonConverter = new JsonConverter(JsonAdapters.object({\n    date: JsonAdapters.isoDate(),\n    set: JsonAdapters.set()\n}));\n\nconst originalObject = {\n    date: new Date(),\n    set: new Set([1, 2, 3])\n};\n\nconst serializedObject = customJsonConverter.stringify(originalObject);\n\nconst deserializedObject = customJsonConverter.parse(serializedObject);\n```\n\nIf you check the value of the `serializedObject` variable, now you'll see the following JSON structure:\n\n```json\n{\n    \"date\": \"2021-05-08T06:45:45.000Z\",\n    \"set\": [\n        1,\n        2,\n        3\n    ]\n}\n```\n\nAs you can see, now your `Set` has been serialized as a JSON array, preserving its values in the JSON structure. In addition, if\nyou check the value of the `deserializedObject` variable, you'll see the following object:\n\n![Deserialized object using TrueJSON (Google Chrome console)](docs/img/deserialized-object-truejson.png \"Deserialized object using TrueJSON (Google Chrome console)\")\n\nAs you can see, both the `date` property and the `set` property have been deserialized to `Date` and `Set` objects respectively.\n\nIn addition, TrueJSON will perform some validations (i.e. type checks) before serializing or deserializing, throwing errors if\nthe received input doesn't match the expected structure.\n\n### Features\n\n* Serialization and deserialization of complex data types - see [Built-in adapters](#built-in-adapters) section.\n* Serialization and deserialization of custom data types - see [Writing your own adapter](#writing-your-own-adapter) section.\n* Input validation on serialization and deserialization.\n* Configurable JSON serializer - see [Using JSON5 and other JSON alternatives](#using-json5-or-other-json-alternatives) section.\n\n## Installation\n\n### Using NPM \\(module\\)\n\nInstall the latest stable version:\n\n```bash\nnpm install --save true-json\n```\n\nThen you can import TrueJSON objects in your modules:\n\n```javascript\nimport { JsonConverter, JsonAdapters } from 'true-json';\n```\n\n### Using `\u003cscript\u003e` tag \\(standalone\\)\n\nYou can [download the latest version from here](dist/true-json.js). Then, you can use it as any other JavaScript file:\n\n```html\n\n\u003cscript src=\"true-json.js\"\u003e\u003c/script\u003e\n```\n\nOr, if you prefer, you can use any of the following CDN repositories:\n\n```html\n\u003c!-- Unpkg --\u003e\n\u003cscript src=\"https://unpkg.com/true-json@1.0.1\"\u003e\u003c/script\u003e\n\n\u003c!-- JsDelivr --\u003e\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/true-json@1.0.1\"\u003e\u003c/script\u003e\n```\n\nThe script will create a global `TrueJSON` object, which contains all the exported objects.\n\n## Basic usage\n\n### Using `import` \\(module\\)\n\n```javascript\nimport { JsonConverter, JsonAdapters } from 'true-json';\n\nconst user = {\n    name: 'John Doe',\n    birthDate: new Date('1970-01-01'),\n    bestScoreByGame: new Map([\n        ['Minesweeper', 118],\n        ['Donkey Kong', 35500],\n        ['Super Mario Bros.', 183250],\n    ])\n};\n\nconst userJsonConverter = new JsonConverter(JsonAdapters.object({\n    birthDate: JsonAdapters.isoDate(),\n    bestScoreByGame: JsonAdapters.mapAsRecord()\n}));\n\nconst userAsJson = userJsonConverter.stringify(user);\n\nconsole.log(userAsJson);\n```\n\n### Using `TrueJSON` object \\(standalone\\)\n\nYou can access any object just by doing `TrueJSON.[ObjectName]`. We strongly recommend\nusing ES6 _destructuring assignment_ to get a similar result to module imports:\n\n```javascript\nconst { JsonConverter, JsonAdapters } = TrueJSON;\n\nconst user = {\n    name: 'John Doe',\n    birthDate: new Date('1970-01-01'),\n    bestScoreByGame: new Map([\n        ['Minesweeper', 118],\n        ['Donkey Kong', 35500],\n        ['Super Mario Bros.', 183250],\n    ])\n};\n\nconst userJsonConverter = new JsonConverter(JsonAdapters.object({\n    birthDate: JsonAdapters.isoDate(),\n    bestScoreByGame: JsonAdapters.mapAsRecord()\n}));\n\nconst userAsJson = userJsonConverter.stringify(user);\n\nconsole.log(userAsJson);\n```\n\n## Using JSON5 or other JSON alternatives\n\nYou can configure TrueJSON's `JsonConverter` to use any custom JSON implementation, such as JSON5. The only requirement is that\nthe custom JSON implementation should have the same `parse()` and `stringify()` methods as the standard one.\n\nLet's see an example using [`json5`'s NPM package](https://www.npmjs.com/package/json5):\n\n```javascript\nimport json5 from 'json5';\nimport { JsonConverter, JsonAdapters } from 'true-json';\n\nconst user = {\n    name: 'John Doe',\n    birthDate: new Date('1970-01-01'),\n    bestScoreByGame: new Map([\n        ['Minesweeper', 118],\n        ['Donkey Kong', 35500],\n        ['Super Mario Bros.', 183250],\n    ])\n};\n\nconst userJsonAdapter = JsonAdapters.object({\n    birthDate: JsonAdapters.isoDate(),\n    bestScoreByGame: JsonAdapters.mapAsRecord()\n});\n\n// The second argument of JsonConverter's constructor\n// allows you to pass a custom JSON implementation.\n// When no value is passed, the standard JSON object is used.\nconst userJsonConverter = new JsonConverter(userJsonAdapter, json5);\n\nconst userAsJson = userJsonConverter.stringify(user);\n\nconsole.log(userAsJson);\n```\n\n## Built-in adapters\n\nIn this section, we will cover the build-in adapters that TrueJSON provides to you.\n\n### isoDate()\n\nThis adapter converts any `Date` object into his ISO textual representation (see\n[`Date.prototype.toISOString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)\ndocs) and vice versa:\n\n```javascript\nconst adapter = JsonAdapters.isoDate();\n\nconsole.log(adapter.adaptToJson(new Date(0)));\n\nconsole.log(adapter.recoverFromJson('1970-01-01T00:00:00.000Z'));\n```\n\nOutput:\n\n```text\n\"1970-01-01T00:00:00.000Z\"\n\nDate { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) }\n```\n\n### dateTimestamp()\n\nThis adapter converts any `Date` object into his number representation in milliseconds (see\n[`Date.prototype.getTime()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime)\ndocs) and vice versa:\n\n```javascript\nconst adapter = JsonAdapters.dateTimestamp();\n\nconsole.log(adapter.adaptToJson(new Date(0)));\n\nconsole.log(adapter.recoverFromJson(0));\n```\n\n```text\n0\n\nDate { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) }\n```\n\n### array(elementAdapter)\n\nUsing this adapter you can specify how the elements of the array should be adapted:\n\n```javascript\nconst adapter = JsonAdapters.array(JsonAdapters.dateTimestamp());\n\nconsole.log(adapter.adaptToJson([\n    new Date(0),\n    new Date(1620458583563)\n]));\n\nconsole.log(adapter.recoverFromJson([\n    0,\n    1620458583563\n]));\n```\n\nOutput:\n\n```text\n[0, 1620458583563]\n\n[\n    Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) },\n    Date { Sat May 08 2021 07:23:03 GMT+0000 (Coordinated Universal Time) }\n]\n```\n\n### set(\\[elementAdapter])\n\nBy default, JavaScript sets are serialized as an empty object. Using this adapter allows you to serialize them in the same way\nthat arrays are serialized:\n\n```javascript\nconst adapter = JsonAdapters.set();\n\nconsole.log(adapter.adaptToJson(new Set([1, 2, 3])));\n\nconsole.log(adapter.recoverFromJson([1, 2, 3]));\n```\n\nOutput:\n\n```text\n[1, 2, 3]\n\nSet { 1, 2, 3 }\n```\n\nYou can also specify an adapter to be used for mapping the elements of the set:\n\n```javascript\nconst adapter = JsonAdapters.set(JsonAdapters.dateTimestamp());\n\nconsole.log(adapter.adaptToJson(new Set([new Date(0), new Date(1620458583563)])));\n\nconsole.log(adapter.recoverFromJson([0, 1620458583563]));\n```\n\nOutput:\n\n```text\n[0, 1620458583563]\n\nSet {\n    Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) },\n    Date { Sat May 08 2021 07:23:03 GMT+0000 (Coordinated Universal Time) }\n}\n```\n\nNotice that calling `JsonAdapters.set()` without any element adapter is equivalent to\n`JsonAdapters.set(JsonAdapters.identity())`.\n\n### record(valueAdapter)\n\nA record is a JavaScript plain object consisting of key-value pairs. In that sense, it's a similar to a `Map` (a.k.a.\n_hashtable_ or _dictionary_ in other programming languages), but its keys are always strings*.\n\n\u003csmall\u003e\u0026ast; JavaScript allows to use the `symbol` type as a key also, but TrueJSON expects records to be in the form\n`{ string: any }` (for TypeScript users: `Record\u003cstring, any\u003e`).\u003c/small\u003e\n\nThe record adapter receives an adapter that will be applied to each of the record values, just as the array adapter does:\n\n```javascript\nconst adapter = JsonAdapters.record(JsonAdapters.dateTimestamp());\n\nconsole.log(adapter.adaptToJson({\n    start: new Date(0),\n    end: new Date(1620458583563)\n}));\n\nconsole.log(adapter.recoverFromJson({\n    start: 0,\n    end: 1620458583563\n}));\n```\n\nOutput:\n\n```text\n{\n    \"start\": 0,\n    \"end\": 1620458583563\n}\n\n{\n    \"start\": Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) },\n    \"end\": Date { Sat May 08 2021 07:23:03 GMT+0000 (Coordinated Universal Time) }\n}\n```\n\n#### Configuration options\n\nThe record adapter allows to modify its default behaviour using the following configuration options:\n\n| Property | Type | Default value | Description |\n|----------|------|---------------|-------------|\n| `strictPlainObjectCheck` | `boolean` | `false` | When `true`, it will throw an error if the input value of the `adaptToJson()` method is not a plain object. |\n\n### mapAsEntries(\\[config])\n\nBy default, JavaScript maps are serialized as an empty object. Using this adapter allows you to serialize them as an array of\nentries (see\n[Map.prototype.entries()](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Map/entries)):\n\n```javascript\nconst map = new Map();\nmap.set('number', 42);\nmap.set('string', 'hello world');\nmap.set('array', [1, 2, 3]);\n\nconst adapter = JsonAdapters.mapAsEntries();\n\nconsole.log(adapter.adaptToJson(map));\n\nconsole.log(adapter.recoverFromJson([\n    ['number', 42],\n    ['string', 'hello world'],\n    ['array', [1, 2, 3]]\n]));\n```\n\nOutput:\n\n```text\n[\n    [\"number\", 42],\n    [\"string\", \"hello world\"],\n    [\"array\", [1, 2, 3]]\n]\n\nMap {\n    \"number\" =\u003e 42,\n    \"string\" =\u003e \"hello world\",\n    \"array\" =\u003e [1, 2, 3]\n}\n```\n\nAs map's keys and values can be any type of object, you can also specify a `keyAdapter` and a `valueAdapter`:\n\n```javascript\nconst map = new Map();\nmap.set(new Date(0), new Set([1, 2, 3]));\nmap.set(new Date(1620458583563), new Set([4, 5, 6]));\n\nconst adapter = JsonAdapters.mapAsEntries({\n    keyAdapter: JsonAdapters.dateTimestamp(),\n    valueAdapter: JsonAdapters.set(),\n});\n\nconsole.log(adapter.adaptToJson(map));\n\nconsole.log(adapter.recoverFromJson([\n    [0, [1, 2, 3]],\n    [1620458583563, [4, 5, 6]]\n]));\n```\n\nOutput:\n\n```text\n[\n    [0, [1, 2, 3]],\n    [1620458583563, [4, 5, 6]]\n]\n\nMap {\n    Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) } =\u003e Set { 1, 2, 3 },\n    Date { Sat May 08 2021 07:23:03 GMT+0000 (Coordinated Universal Time) } =\u003e Set { 4, 5, 6 }\n}\n```\n\nNotice that calling `JsonAdapters.mapAsEntries()` without key and value adapters is equivalent to:\n\n```javascript\nJsonAdapters.mapAsEntries({\n    keyAdapter: JsonAdapters.identity(),\n    valueAdapter: JsonAdapters.identity()\n});\n```\n\n### mapAsRecord(\\[config])\n\nBy default, JavaScript maps are serialized as an empty object. Using this adapter allows you to serialize them as a plain JS\nobject (a.k.a. record):\n\n```javascript\nconst map = new Map();\nmap.set('number', 42);\nmap.set('string', 'hello world');\nmap.set('array', [1, 2, 3]);\n\nconst adapter = JsonAdapters.mapAsRecord();\n\nconsole.log(adapter.adaptToJson(map));\n\nconsole.log(adapter.recoverFromJson({\n    number: 42,\n    string: 'hello world',\n    array: [1, 2, 3]\n}));\n```\n\nOutput:\n\n```text\n{\n    \"number\": 42,\n    \"string\": \"hello world\",\n    \"array\": [1, 2, 3]\n}\n\nMap {\n    \"number\" =\u003e 42,\n    \"string\" =\u003e \"hello world\",\n    \"array\" =\u003e [1, 2, 3]\n}\n```\n\nAs map's keys and values can be any type of object, you can also specify a `keyAdapter` and a `valueAdapter`:\n\n```javascript\nconst map = new Map();\nmap.set(new Date(0), new Set([1, 2, 3]));\nmap.set(new Date(1620458583563), new Set([4, 5, 6]));\n\nconst adapter = JsonAdapters.mapAsRecord({\n    keyAdapter: JsonAdapters.isoDate(),\n    valueAdapter: JsonAdapters.set(),\n});\n\nconsole.log(adapter.adaptToJson(map));\n\nconsole.log(adapter.recoverFromJson({\n    \"1970-01-01T00:00:00.000Z\": [1, 2, 3],\n    \"2021-05-08T07:23:03.563Z\": [4, 5, 6]\n}));\n```\n\nOutput:\n\n```text\n{\n    \"1970-01-01T00:00:00.000Z\": [1, 2, 3],\n    \"2021-05-08T07:23:03.563Z\": [4, 5, 6]\n}\n\nMap {\n    Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) } =\u003e Set { 1, 2, 3 },\n    Date { Sat May 08 2021 07:23:03 GMT+0000 (Coordinated Universal Time) } =\u003e Set { 4, 5, 6 }\n}\n```\n\nNotice that calling `JsonAdapters.mapAsRecord()` without key and value adapters is equivalent to:\n\n```javascript\nJsonAdapters.mapAsRecord({\n    keyAdapter: JsonAdapters.identity(),\n    valueAdapter: JsonAdapters.identity()\n});\n```\n\n### object(propertyAdapters\\[, config])\n\nThis adapter allows you to serialize \u0026amp; deserialize any plain JS object, specifying different adapters for each of its\nproperties:\n\n```javascript\nconst film = {\n    name: 'Harry Potter and the Deathly Hallows - Part 2',\n    releaseDate: new Date('2011-07-15'),\n    mainCharacters: new Set(['Harry Potter', 'Hermione Granger', 'Ron Weasley'])\n};\n\nconst adapter = JsonAdapters.object({\n    releaseDate: JsonAdapters.isoDate(),\n    mainCharacters: JsonAdapters.set()\n});\n\nconsole.log(adapter.adaptToJson(film));\n\nconsole.log(adapter.recoverFromJson({\n    name: 'Harry Potter and the Deathly Hallows - Part 2',\n    releaseDate: '2011-07-15T00:00:00.000Z',\n    mainCharacters: ['Harry Potter', 'Hermione Granger', 'Ron Weasley']\n}));\n```\n\nOutput:\n\n```text\n{\n    \"name\": \"Harry Potter and the Deathly Hallows - Part 2\",\n    \"releaseDate\": \"2011-07-15T00:00:00.000Z\",\n    \"mainCharacters\": [\"Harry Potter\", \"Hermione Granger\", \"Ron Weasley\"]\n}\n\n{\n    \"name\": \"Harry Potter and the Deathly Hallows - Part 2\",\n    \"releaseDate\": Date { Fri Jul 15 2011 00:00:00 GMT+0000 (Coordinated Universal Time) },\n    \"mainCharacters\": Set { \"Harry Potter\", \"Hermione Granger\", \"Ron Weasley\" }\n}\n```\n\nBy default, any unmapped property will be adapted using the [identity adapter](#identityvalidator).\n\n#### Configuration options\n\nThe object adapter allows to modify its default behaviour using the following configuration options:\n\n| Property | Type | Default value | Description |\n|----------|------|---------------|-------------|\n| `omitUnmappedProperties` | `boolean` | `false` | When `true`, all unmapped properties won't be present on the resultant object. |\n| `omittedProperties` | `string[]` | `[]` | Allows to specify which properties should be omitted manually. |\n| `strictPlainObjectCheck` | `boolean` | `false` | When `true`, it will throw an error if the input value of the `adaptToJson()` method is not a plain object. |\n\nExample using `omittedProperties` option:\n\n```javascript\nconst adapter = JsonAdapters.object(\n        {\n            birthDate: JsonAdapters.dateTimestamp()\n        },\n        {\n            omittedProperties: ['age']\n        }\n);\n\nconsole.log(adapter.adaptToJson({\n    name: 'John Doe',\n    birthDate: new Date('1970-01-01'),\n    age: 51\n}));\n\nconsole.log(adapter.recoverFromJson({\n    name: 'John Doe',\n    birthDate: 0,\n    age: 51\n}));\n```\n\nOutput:\n\n```text\n{\n    \"name\": \"John Doe\",\n    \"birthDate\": 0\n}\n\n{\n    \"name\": \"John Doe\",\n    \"birthDate\": Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) }\n}\n```\n\n### byKey(keyValuePairs)\n\nThis adapter allows you to serialize a value using its corresponding key of the provided key-value pairs object. This is\nspecially useful when working with enumerated values:\n\n```javascript\nconst ScalingStrategies = {\n    DEFAULT: new DefaultScalingStrategy(),\n    FAST: new FastScalingStrategy(),\n    SMOOTH: new SmoothScalingStrategy()\n};\n\nconst adapter = JsonAdapters.byKey(ScalingStrategies);\n\nconsole.log(adapter.adaptToJson(ScalingStrategies.FAST));\n\nconsole.log(adapter.recoverFromJson('SMOOTH'));\n```\n\nOutput:\n\n```text\n\"FAST\"\n\nSmoothScalingStrategy { }\n```\n\nIf any unknown value is passed to `adaptToJson()` or `recoverFromJson()` methods, an error is thrown.\nIf you want to prevent this, you can use the\n[`byKeyLenient(keyValuePairs\\[, fallbackKey\\])`](#bykeylenientkeyvaluepairs-fallbackkey) method.\n\n### byKeyLenient(keyValuePairs\\[, fallbackKey])\n\nThis is very similar to [`byKey(keyValuePairs)`](#bykeykeyvaluepairs)'s adapter. The main difference is the case where the\npassed value is not present in the `keyValuePairs` object. While the [`byKey(keyValuePairs)`](#bykeykeyvaluepairs) adapter will\nthrow an error, this adapter will return `undefined`:\n\n```javascript\nconst ScalingStrategies = {\n    DEFAULT: new DefaultScalingStrategy(),\n    FAST: new FastScalingStrategy(),\n    SMOOTH: new SmoothScalingStrategy()\n};\n\nconst adapter = JsonAdapters.byKeyLenient(ScalingStrategies);\n\nconsole.log(adapter.adaptToJson(new UnknownScalingStrategy()));\n\nconsole.log(adapter.recoverFromJson('UNKNOWN'));\n```\n\nOutput:\n\n```text\nundefined\n\nundefined\n```\n\nDue to this, `undefined` value is also a valid input for both `adaptToJson()` and `recoverFromJson()` methods:\n\n```javascript\nconst ScalingStrategies = {\n    DEFAULT: new DefaultScalingStrategy(),\n    FAST: new FastScalingStrategy(),\n    SMOOTH: new SmoothScalingStrategy()\n};\n\nconst adapter = JsonAdapters.byKeyLenient(ScalingStrategies);\n\nconsole.log(adapter.adaptToJson(undefined));\n\nconsole.log(adapter.recoverFromJson(undefined));\n```\n\nOutput:\n\n```text\nundefined\n\nundefined\n```\n\nAlternatively, you can pass a fallback key to use when an unknown object or key is passed to the adapter:\n\n```javascript\nconst ScalingStrategies = {\n    DEFAULT: new DefaultScalingStrategy(),\n    FAST: new FastScalingStrategy(),\n    SMOOTH: new SmoothScalingStrategy()\n};\n\nconst adapter = JsonAdapters.byKeyLenient(ScalingStrategies, 'DEFAULT');\n\nconsole.log(adapter.adaptToJson(new UnknownScalingStrategy()));\n\nconsole.log(adapter.recoverFromJson('UNKNOWN'));\n```\n\nOutput:\n\n```text\n\"DEFAULT\"\n\nDefaultScalingStrategy { }\n```\n\n### Identity adapters\n\nIdentity adapters take their name from the concept of _identity function_. Those adapters doesn't really adapt its input\nvalue, but they're still able to perform some validations that will ensure the received JSON has the expected format.\nThe following sections will cover the different identity adapters provided by TrueJSON out-of-the-box.\n\n#### identity(\\[validator])\n\nThe identity adapter takes its name from the concept of _identity function_. It just returns the same value it receives:\n\n```javascript\nconst adapter = JsonAdapters.identity();\n\nconsole.log(adapter.adaptToJson(3));\n\nconsole.log(adapter.recoverFromJson(3));\n```\n\nOutput:\n\n```text\n3\n\n3\n```\n\nBy default, it doesn't perform any validations, but it accepts to pass a validator function, allowing you to implement\nany validation logic that you may need:\n\n```javascript\nconst probabilityAdapter = JsonAdapters.identity(input =\u003e {\n\n    if (typeof input !== 'number' || !Number.isFinite()) {\n        throw new TypeError('input value is not a finite number');\n    }\n\n    if(input \u003c 0 || input \u003e 1) {\n        throw new RangeError('input value is out of [0, 1] range');\n    }\n\n});\n\nconsole.log(probabilityAdapter.adaptToJson('A text'));\nconsole.log(probabilityAdapter.adaptToJson(3));\nconsole.log(probabilityAdapter.adaptToJson(0.3));\n\nconsole.log(probabilityAdapter.recoverFromJson(Infinity));\nconsole.log(probabilityAdapter.recoverFromJson(-1));\nconsole.log(probabilityAdapter.recoverFromJson(0.95));\n```\n\nOutput:\n\n```text\nTypeError: input value is not a finite number\nRangeError: input value is out of [0, 1] range\n0.3\n\nTypeError: input value is not a finite number\nRangeError: input value is out of [0, 1] range\n0.95\n```\n\nAdditionally, in the following sections you'll find some other identity adapters that perform some of the most-common\ntype validations.\n\n#### stringIdentity()\n\nThe string identity adapter simply returns the same value it receives, throwing an error if the value is not a string:\n\n```javascript\nconst stringIdentityAdapter = JsonAdapters.stringIdentity();\n\nconsole.log(stringIdentityAdapter.adaptToJson('A text'));\nconsole.log(stringIdentityAdapter.adaptToJson(3));\n\nconsole.log(stringIdentityAdapter.recoverFromJson('Another text'));\nconsole.log(stringIdentityAdapter.recoverFromJson(['an', 'array']));\n```\n\nOutput:\n\n```text\n\"A text\"\nTypeError: input value is not a string\n\n\"Another text\"\nTypeError: input value is not a string\n```\n\n#### numberIdentity()\n\nThe number identity adapter simply returns the same value it receives, throwing an error if the value is not a finite\nnumber (this means that non-finite values like `Infinite`, `-Infinite` or `NaN` are not valid):\n\n```javascript\nconst numberIdentityAdapter = JsonAdapters.numberIdentity();\n\nconsole.log(numberIdentityAdapter.adaptToJson(1234));\nconsole.log(numberIdentityAdapter.adaptToJson(Infinity));\n\nconsole.log(numberIdentityAdapter.recoverFromJson(-5.7));\nconsole.log(numberIdentityAdapter.recoverFromJson({an: 'object'}));\n```\n\nOutput:\n\n```text\n1234\nTypeError: input value is not a finite number\n\n-5.7\nTypeError: input value is not a finite number\n```\n\n#### integerIdentity()\n\nThe integer identity adapter simply returns the same value it receives, throwing an error if the value is not an integer\nnumber (this means that decimal numbers and non-finite values like `12.34`, `Infinite`, `-Infinite` or `NaN` are not\nvalid):\n\n```javascript\nconst integerIdentityAdapter = JsonAdapters.integerIdentity();\n\nconsole.log(integerIdentityAdapter.adaptToJson(1234));\nconsole.log(integerIdentityAdapter.adaptToJson(12.34));\n\nconsole.log(integerIdentityAdapter.recoverFromJson(-5));\nconsole.log(integerIdentityAdapter.recoverFromJson(NaN));\n```\n\nOutput:\n\n```text\n1234\nTypeError: input value is not an integer\n\n-5\nTypeError: input value is not an integer\n```\n\n#### booleanIdentity()\n\nThe boolean identity adapter simply returns the same value it receives, throwing an error if the value is not a boolean:\n\n```javascript\nconst booleanIdentityAdapter = JsonAdapters.booleanIdentity();\n\nconsole.log(booleanIdentityAdapter.adaptToJson(true));\nconsole.log(booleanIdentityAdapter.adaptToJson('true'));\n\nconsole.log(booleanIdentityAdapter.recoverFromJson(false));\nconsole.log(booleanIdentityAdapter.recoverFromJson(0));\n```\n\nOutput:\n\n```text\ntrue\nTypeError: input value is not a boolean\n\nfalse\nTypeError: input value is not a boolean\n```\n\n### Handling nullish values\n\nAccording to [MDN](https://developer.mozilla.org/en-US/docs/Glossary/Nullish):\n\n\u003e In JavaScript, a nullish value is the value which is either `null` or `undefined`.\n\nAdapters discussed in previous sections are not designed taking _nullish values_ into account. If you try to use them\nfor serializing or deserializing `null` or `undefined` values, they could throw an error or return an unexpected value.\nLet's take a look to the behaviour of the set adapter:\n\n```javascript\nconst standardSetAdapter = JsonAdapters.set();\n\nconsole.log(standardSetAdapter.adaptToJson(new Set([1, 2, 3])));\nconsole.log(standardSetAdapter.adaptToJson(null));\nconsole.log(standardSetAdapter.adaptToJson(undefined));\n\nconsole.log(standardSetAdapter.recoverFromJson([1, 2, 3]));\nconsole.log(standardSetAdapter.recoverFromJson(null));\nconsole.log(standardSetAdapter.recoverFromJson(undefined));\n```\n\nOutput:\n\n```text\n[1, 2, 3]\nTypeError: o is not iterable\nTypeError: o is not iterable\n\nSet { 1, 2, 3 }\nTypeError: Cannot read properties of null (reading 'map')\nTypeError: Cannot read properties of undefined (reading 'map')\n```\n\nThe same applies when you [write your own adapters](#writing-your-own-adapter). If you don't write your adapter having\n_nullish values_ in mind, it may not work as expected when receiving one.\n\nFortunately, TrueJSON allows to wrap any existing adapter using a _proxy adapter_ that handles `null` and `undefined`\nvalues:\n\n#### nullishAware(adapter)\n\nWraps an existing adapter using a proxy that handles both `null` and `undefined` values. This proxy will return the\nreceived value when it's a _nullish value_; otherwise it will call the real adapter:\n\n```javascript\nconst nullishAwareSetAdapter = JsonAdapters.set();\n\nconsole.log(nullishAwareSetAdapter.adaptToJson(new Set([1, 2, 3])));\nconsole.log(nullishAwareSetAdapter.adaptToJson(null));\nconsole.log(nullishAwareSetAdapter.adaptToJson(undefined));\n\nconsole.log(nullishAwareSetAdapter.recoverFromJson([1, 2, 3]));\nconsole.log(nullishAwareSetAdapter.recoverFromJson(null));\nconsole.log(nullishAwareSetAdapter.recoverFromJson(undefined));\n```\n\nOutput:\n\n```text\n[1, 2, 3]\nnull\nundefined\n\nSet { 1, 2, 3 }\nnull\nundefined\n```\n\n#### nullAware(adapter)\n\nWraps an existing adapter using a proxy that handles only `null` values. This proxy will return `null` when receiving\nthe `null` value; otherwise it will call the real adapter:\n\n```javascript\nconst nullAwareSetAdapter = JsonAdapters.nullAware(JsonAdapters.set());\n\nconsole.log(nullAwareSetAdapter.adaptToJson(new Set([1, 2, 3])));\nconsole.log(nullAwareSetAdapter.adaptToJson(null));\nconsole.log(nullAwareSetAdapter.adaptToJson(undefined));\n\nconsole.log(nullAwareSetAdapter.recoverFromJson([1, 2, 3]));\nconsole.log(nullAwareSetAdapter.recoverFromJson(null));\nconsole.log(nullAwareSetAdapter.recoverFromJson(undefined));\n```\n\nOutput:\n\n```text\n[1, 2, 3]\nnull\nTypeError: o is not iterable\n\nSet { 1, 2, 3 }\nnull\nTypeError: Cannot read properties of undefined (reading 'map')\n```\n\nNotice an error is thrown when using the `undefined` value.\n\n#### undefinedAware(adapter)\n\nWraps an existing adapter using a proxy that handles only `undefined` values. This proxy will return `undefined` when\nreceiving the `undefined` value; otherwise it will call the real adapter:\n\n```javascript\nconst undefinedAwareSetAdapter = JsonAdapters.undefinedAware(JsonAdapters.set());\n\nconsole.log(undefinedAwareSetAdapter.adaptToJson(new Set([1, 2, 3])));\nconsole.log(undefinedAwareSetAdapter.adaptToJson(null));\nconsole.log(undefinedAwareSetAdapter.adaptToJson(undefined));\n\nconsole.log(undefinedAwareSetAdapter.recoverFromJson([1, 2, 3]));\nconsole.log(undefinedAwareSetAdapter.recoverFromJson(null));\nconsole.log(undefinedAwareSetAdapter.recoverFromJson(undefined));\n```\n\nOutput:\n\n```text\n[1, 2, 3]\nTypeError: o is not iterable\nundefined\n\nSet { 1, 2, 3 }\nTypeError: Cannot read properties of null (reading 'map')\nundefined\n```\n\nNotice an error is thrown when using the `null` value.\n\n## Writing your own adapter\n\nYou can write your own adapter using the `JsonAdapters.custom()` method:\n\n```javascript\nconst dateToArrayAdapter = JsonAdapters.custom({\n    adaptToJson(date) {\n\n        // Perform some validations\n\n        if (!(date instanceof Date)) {\n            throw new TypeError('input value is not a date');\n        }\n\n        // Adapt the Date object to array\n\n        return [\n            date.getFullYear(),\n            date.getMonth() + 1,\n            date.getDate()\n        ];\n\n    },\n    recoverFromJson(array) {\n\n        // Perform some validations\n\n        if (!Array.isArray(array)) {\n            throw new TypeError('input value is not an array');\n        }\n\n        if (array.length !== 3) {\n            throw new TypeError('input value has not the expected length');\n        }\n\n        // Recover the Date object from the deserialized array\n\n        const [\n            year,\n            month,\n            date\n        ] = array;\n\n        return new Date(year, month - 1, date);\n\n    }\n});\n```\n\nThen, you can use it as any other adapter:\n\n```javascript\nconst objectAdapter = JsonAdapters.object({\n    birthDate: dateToArrayAdapter\n});\n\nconsole.log(objectAdapter.adaptToJson({\n    name: 'John Doe',\n    birthDate: new Date('1970-01-01')\n}));\n\nconsole.log(objectAdapter.recoverFromJson({\n    name: 'John Doe',\n    birthDate: [1970, 1, 1]\n}));\n```\n\nOutput:\n\n```text\n{\n    \"name\": \"John Doe\",\n    \"birthDate\": [1970, 1, 1]\n}\n\n{\n    \"name\": \"John Doe\",\n    \"birthDate\": Date { Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time) }\n}\n```\n\n## Contributing\n\nThis is a library maintained by one person, so any bug report, suggestion, pull request, or any other kind of feedback will be\nreally appreciated :slightly_smiling_face:\n\nPlease contribute using [GitHub Flow](https://guides.github.com/introduction/flow). Create a branch from the `develop`\none, add commits, and [open a pull request](https://github.com/nestorrente/true-json/compare).\n\nPlease note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.\n\nIf you want to get in touch with the author, you can contact me through\n[LinkedIn](https://www.linkedin.com/in/nestorpglez/) or [email](mailto:nestorpglez@gmail.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnestorrente%2Ftrue-json","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnestorrente%2Ftrue-json","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnestorrente%2Ftrue-json/lists"}