{"id":13447396,"url":"https://github.com/11ways/json-dry","last_synced_at":"2025-04-05T05:05:34.089Z","repository":{"id":13221452,"uuid":"15905740","full_name":"11ways/json-dry","owner":"11ways","description":"🌞 JSON-dry allows you to serialize \u0026 revive objects containing circular references, dates, regexes, class instances,...","archived":false,"fork":false,"pushed_at":"2024-07-25T18:39:49.000Z","size":127,"stargazers_count":229,"open_issues_count":0,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-08-05T08:07:07.157Z","etag":null,"topics":["circular-references","javascript","json","json-dry","json-parser","json-serialization"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/11ways.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"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}},"created_at":"2014-01-14T15:17:12.000Z","updated_at":"2024-08-05T08:07:14.021Z","dependencies_parsed_at":"2023-02-13T02:31:19.850Z","dependency_job_id":"16ad950a-e405-4d70-a12a-3401a0968d58","html_url":"https://github.com/11ways/json-dry","commit_stats":{"total_commits":96,"total_committers":4,"mean_commits":24.0,"dds":"0.44791666666666663","last_synced_commit":"4cc2f4cd29c3b86d466e206879517621e0f2e389"},"previous_names":["skerit/json-dry"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/11ways%2Fjson-dry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/11ways%2Fjson-dry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/11ways%2Fjson-dry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/11ways%2Fjson-dry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/11ways","download_url":"https://codeload.github.com/11ways/json-dry/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289426,"owners_count":20914464,"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":["circular-references","javascript","json","json-dry","json-parser","json-serialization"],"created_at":"2024-07-31T05:01:16.419Z","updated_at":"2025-04-05T05:05:34.067Z","avatar_url":"https://github.com/11ways.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cb\u003eJSON-DRY\u003c/b\u003e\n\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003c!-- CI - Github Actions --\u003e\n  \u003ca href=\"https://github.com/11ways/json-dry/actions/workflows/unit_test.yaml\"\u003e\n    \u003cimg src=\"https://github.com/11ways/json-dry/actions/workflows/unit_test.yaml/badge.svg\" alt=\"Node.js CI (Linux, MacOS, Windows)\" /\u003e\n  \u003c/a\u003e\n\n  \u003c!-- Coverage - Codecov --\u003e\n  \u003ca href=\"https://codecov.io/gh/11ways/json-dry\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/github/11ways/json-dry/master.svg\" alt=\"Codecov Coverage report\" /\u003e\n  \u003c/a\u003e\n\n  \u003c!-- DM - Snyk --\u003e\n  \u003ca href=\"https://snyk.io/test/github/11ways/json-dry?targetFile=package.json\"\u003e\n    \u003cimg src=\"https://snyk.io/test/github/11ways/json-dry/badge.svg?targetFile=package.json\" alt=\"Known Vulnerabilities\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003c!-- Version - npm --\u003e\n  \u003ca href=\"https://www.npmjs.com/package/json-dry\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/json-dry.svg\" alt=\"Latest version on npm\" /\u003e\n  \u003c/a\u003e\n\n  \u003c!-- License - MIT --\u003e\n  \u003ca href=\"https://github.com/11ways/json-dry#license\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/11ways/json-dry.svg\" alt=\"Project license\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\u003cdiv align=\"center\"\u003e\n  Serialize objects while preserving references and custom class instances\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003csub\u003e\n    Coded with ❤️ by \u003ca href=\"#authors\"\u003eEleven Ways\u003c/a\u003e.\n  \u003c/sub\u003e\n\u003c/div\u003e\n\n## Table of contents\n\n  * [Installation](#installation)\n  * [Usage](#usage)\n    * [Basic example](#basic-example)\n    * [Implementing methods for serializing \u0026 reviving instances](#implementing-methods-for-serializing--reviving-instances)\n    * [toObject](#toobject)\n  * [Cloning objects \u0026 instances](#cloning-objects--instances)\n    * [Clone methods](#clone-methods)\n      * [dryClone](#dryclone)\n      * [Custom clone methods](#custom-clone-methods)\n  * [Project history](#project-history)\n  * [Versioning](#versioning)\n  * [License](#license)\n  * [Acknowledgments](#acknowledgments)\n\n## Version 2.x!\n\nFirst of all: **Version 2.x of `json-dry` is not able to parse output from version 1.x, if that output contains references!**\nThe way references are made \u0026 revived has changed completely.\n\nAll other syntax has remained the same.\n\nIf you did not use `json-dry` to store serialized objects long-term (so just on-the-fly, for communication) then it's probably safe to upgrade.\n\n## Installation\n\n    $ npm install json-dry\n\n\n## Usage\n\n### Basic example\n\nThis is a basic example of stringifying an object (containing multiple references to the same object) and parsing it again.\n\n```js\nlet Dry = require('json-dry');\n\n// The object we'll serialize later\nlet obj = {};\n\n// The object we'll make multiple references to\nlet ref = {\n    date  : new Date(),\n    regex : /test/i\n};\n\n// Now we'll make multiple references:\n// `reference_one` and `reference_two` both point to the same object\n// `date` refers to a `Date` object\nobj.reference_one = ref;\nobj.reference_two = ref;\nobj.date = ref.date;\n\n// Stringify the object\nlet dried = Dry.stringify(obj);\n// {\n//     \"~refs\": [\n//         {\n//             \"date\": {\"~r\": 1},\n//             \"regex\": {\n//                 \"dry\": \"regexp\",\n//                 \"value\": \"/test/i\"\n//             }\n//         },\n//         {\n//             \"dry\": \"date\",\n//             \"value\": \"2023-01-14T12:00:35.194Z\"\n//         }\n//     ],\n//     \"~root\": {\n//         \"reference_one\": {\"~r\": 0},\n//         \"reference_two\": {\"~r\": 0},\n//         \"date\": {\"~r\": 1}\n//     }\n// }\n\n// Now we'll revive it again\nlet undried = Dry.parse(dried);\n// { reference_one: { date: 2018-01-14T17:56:43.149Z, regex: /test/i },\n//   reference_two: { date: 2018-01-14T17:56:43.149Z, regex: /test/i },\n//   date: 2018-01-14T17:58:50.427Z }\n\n// See if they're the same objects (as it should)\nundried.reference_one == undried.reference_two;\n// true\n\n// The date outside of the reference object is also the same reference\nundried.reference_one.date == undried.date;\n// true\n```\n\n\n### Implementing methods for serializing \u0026 reviving instances\n\nLet's create an example class you might want to serialize and revive:\n\n```js\n// The class constructor\nfunction Person(options) {\n    this.firstname = options.firstname;\n    this.lastname = options.lastname;\n}\n\n// A simple method that prints out the full name\nPerson.prototype.fullname = function fullname() {\n    return this.firstname + ' ' + this.lastname;\n};\n\n// Create an object\nlet jelle = new Person({firstname: 'Jelle', lastname: 'De Loecker'});\n\n// Test out the fullname method\njelle.fullname();\n// returns \"Jelle De Loecker\"\n```\n\nSo now we've created a very basic class, let's register the class and add the **2** required methods for serializing \u0026 reviving.\n\n```js\n// We need to register the class\nDry.registerClass(Person);\n\n// Add the `toDry` method that will be called upon when serializing/stringifying\nPerson.prototype.toDry = function toDry() {\n    return {\n        value: {\n            firstname : this.firstname,\n            lastname  : this.lastname\n        }\n    };\n};\n\n// Now add the `unDry` method as a **static** method, on the constructor\nPerson.unDry = function unDry(value) {\n    // How you do this is up to you.\n    // You can call the constructor for this simple class,\n    // or you can use Object.create, ...\n    var result = new Person(value);\n    return result;\n};\n```\n\nNow let's try stringifying it:\n\n```js\nlet dried = Dry.stringify(jelle);\n// {\"value\":{\"firstname\":\"Jelle\",\"lastname\":\"De Loecker\"},\"dry_class\":\"Person\",\"dry\":\"toDry\",\"drypath\":[]}\n\n// And parse it again\nlet undried = Dry.parse(dried);\n// Person { firstname: 'Jelle', lastname: 'De Loecker' }\n\n// And it works\nundried.fullname();\n// returns \"Jelle De Loecker\"\n```\n\n## Serializing \u0026 reviving instances with circular references\n\nSome classes contain references to each other, for example:\n\n```js\nlet alpha = new Alpha(),\n    beta = new Beta();\n\nalpha.beta = beta;\nbeta.alpha = alpha;\n```\n\nThe problem is that when you serialize \u0026 then try to revive this, one of the `unDry` methods will receive an un-revived placeholder. This can obviously cause issues, especially when setting the property has side-effects. So a new argument `whenDone` has been added to the `unDry` method, like so:\n\n```js\nAlpha.prototype.unDry = function unDry(obj, custom_method, whenDone) {\n\n  let alpha = new Alpha();\n\n  whenDone(function() {\n    alpha.beta = obj.beta;\n  });\n\n  return alpha;\n}\n```\n\n`whenDone` functions will be called just before the `Dry.undry()` function exits, so all the references will have been revived by then.\n\n### toObject\n\nWhile `Dry.stringify` will return you with a json-valid string, `Dry.toObject` will give you a valid simplified object.\n\nIn fact: `Dry.stringify` is just a function that performs `JON.stringify` on `Dry.toObject`'s output.\n\n**Why would you want to use this?** Things like `Workers` and `IndexedDB` communicate data using the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). So instead of performing expensive stringify operations you can just use these objects.\n\n\n## Cloning objects \u0026 instances\n\nJSON-Dry offers a specialized `clone` method. While in theory you could clone an object by drying end reviving it, like so:\n\n```js\nlet cloned = Dry.parse(Dry.toObject(jelle))\n```\n\nThis is a lot slower than using `clone`, because `toObject` needs to do extra work that can be ignored when cloning:\n\n```js\nlet cloned = Dry.clone(jelle);\n```\n\n\n### Clone methods\n\nIf you've added a `toDry` and `unDry` method to your class, by default the `clone` method will use those to create the clone.\n\nHowever, you can also create another method that gets precedence:\n\n\n#### dryClone\n\n```js\nPerson.prototype.dryClone = function dryClone(seen_map, custom_method) {\n    return new Person({\n        firstname : this.firstname,\n        lastname  : this.lastname\n    });\n}    \n```\n\n\n#### Custom clone methods\n\nThe `clone` method takes an extra parameter called `custom_method`. If you're cloning something that has a function property with the same name, that'll be used.\n\nThis can be used when you want to redact certain parts, for example:\n\n```js\nPerson.prototype.specialOccasionClone = function specialOccasionClone(seen_map, custom_method) {\n    return new Person({\n        firstname : this.firstname[0] + '.', // Only add the first letter of the name\n        lastname  : this.lastname\n    });\n};\n\nlet special_clone = Dry.clone(jelle, 'specialOccasionClone');\nspecial_clone.fullname();\n// Returns \"J. De Loecker\"\n```\n\n## Project history\n\nEarlier versions of the project were heavily based on [circular-json](https://github.com/WebReflection/circular-json), a small library that adds (circular) reference support to JSON.\n\nA lot of the JavaScript code on my websites was already shared between the server \u0026 client side, but I also wanted an easy way of sending data to the client while retaining references \u0026 class instances, so I started adding features to circular-json and called it `json-dry` (*dry* as in *don't repeat yourself*).\n\nThe versions of `json-dry` before `2.0.0` used references to the path where the object was first seen, like `~paths~to~the~first~reference`. Unfortunately sometimes objects were nested so deep that these reference paths were a lot longer than the serialized version of the object itself.\n\nThat's why in this new version, objects that are used more than once are stored in the `~refs` array. This way all references to objects can be simple numbers, instead of paths.\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/skerit/json-dry/releases).\n\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F11ways%2Fjson-dry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F11ways%2Fjson-dry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F11ways%2Fjson-dry/lists"}