{"id":24929999,"url":"https://github.com/happycodelucky/json-decoder-node","last_synced_at":"2026-05-08T10:34:45.178Z","repository":{"id":42439839,"uuid":"121552634","full_name":"happycodelucky/json-decoder-node","owner":"happycodelucky","description":"json-decoder allows TypeScript and JavaScript projects to adorn class declarations with JSON decoding decorators to support automatic decoding and marshalling of JSON objects to full prototype objects.","archived":false,"fork":false,"pushed_at":"2022-04-06T04:02:03.000Z","size":568,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-26T01:03:21.768Z","etag":null,"topics":["decoder","deserialization","deserializer","json","nodejs","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/happycodelucky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-14T19:34:31.000Z","updated_at":"2023-11-27T01:03:30.000Z","dependencies_parsed_at":"2022-08-24T18:51:59.182Z","dependency_job_id":null,"html_url":"https://github.com/happycodelucky/json-decoder-node","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/happycodelucky/json-decoder-node","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycodelucky%2Fjson-decoder-node","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycodelucky%2Fjson-decoder-node/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycodelucky%2Fjson-decoder-node/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycodelucky%2Fjson-decoder-node/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/happycodelucky","download_url":"https://codeload.github.com/happycodelucky/json-decoder-node/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycodelucky%2Fjson-decoder-node/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32776898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["decoder","deserialization","deserializer","json","nodejs","typescript"],"created_at":"2025-02-02T13:52:39.430Z","updated_at":"2026-05-08T10:34:45.153Z","avatar_url":"https://github.com/happycodelucky.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# JSON Decoder\n\n![TypeScript Version](https://img.shields.io/badge/typescript-2.9.2-blue.svg)\n![Code Style](https://img.shields.io/badge/codestyle-TypeScript-green.svg)\n![](https://img.shields.io/badge/node-10.5.2-green.svg)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/pryomoax/json-decoder/master/LICENSE)\n\n`json-decoder` allows TypeScript and JavaScript projects to adorn class declarations with JSON decoding decorators to support automatic decoding and marshalling of JSON objects to full prototype objects.\n\n* [Installation](#installation)\n* [Reflect Metadata](#reflect-metadata)\n* [Simple Decoding](#simple-decoding)\n* [Property Aliases](#property-aliases)\n* [Type Marshalling](#type-marshalling---jsontype)\n  * [Collection Marshalling](#collection-marshalling)\n  * [Reverse Array Marshaling](#reverse-array-marshaling)\n  * [Custom Marshaling](#custom-marshaling)\n* [Custom Constructors](#custom-constructors)\n* [JSON Schema Validation](#json-schema-validation)\n\n## JavaScript Can Use JSON?\n\nOf course it can, and the CommonJS module loader of Node.js even makes it easy to use `require('file.json')` so even loading JSON is easy!\n\nWith ES6, JavaScript introduced classes. And TypeScript uses classes way more as a first class citizen. Well in JavaScript it's simply to adopt a class prototype, and in TypeScript interfaces give JSON types.\n\nIt seems like there's isn't too much utility in `json-decoder`? These existing mechanisms are just fine if they work for you, but there are limitations of JSON:\n- You get everything, not just what you want (all those extra properties)\n- JSON properties might not be named appropriately or desired for your class needs\n- JSON properties are limited in types, no support for `Map`, `Set`, `URL`, `Dates` or custom types\n- JSON property values might need coercing, where there is a `String`, maybe a `Number`, or an object needs to be a custom object\n- You may need a single property value pulled up from a sub-object property, or a single element of an array.\n- Custom translation functions called to covert values\n- And more...\n\n## Usage\n\n### Installation\n\nTo install the module use either  [`yarn`](https://yarnpkg.com) or [`npm`](npmjs.org)\n\n#### yarn\n\n\u003e ```bash\n\u003e $ yarn add https://github.com/happycodelucky/json-decoder-node.git\n\u003e ```\n\n#### npm\n\n\u003e ```bash\n\u003e $ npm install -S https://github.com/happycodelucky/json-decoder-node.git\n\u003e ```\n\n\n### Reflect Metadata\n\n[Reflect metadata](https://github.com/rbuckton/reflect-metadata) additions are not yet formally part of EcmaScript so are not available in JavaScript or Node.js. `reflect-metadata` is a package that must only be installed once per project. As such, it's is included here as a peer dependency.\n\nTo install in any project, use:\n\n\u003e ```bash\n\u003e $ yarn add reflect-metadata\n\u003e ```\n\nTo install in any model, use:\n\n\u003e ```bash\n\u003e $ yarn add --peer reflect-metadata\n\u003e ```\n\n## Simple Decoding\n\nSimplest of all declarations is to declare a class as JSON decodable by using the `@jsonDecoable` class decorator. This is required to declare decoding support.\n\nPrototype properties can be decorated with `@jsonProperty`, inheriting the prototype property name as the name of the JSON property to map the value to.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonProperty } from './json-decoder'\n\u003e\n\u003e // Decodable Account class\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // Mapped 'id' property\n\u003e   @jsonProperty\n\u003e   public readonly id: number\n\u003e\n\u003e   // Mapped 'name' property\n\u003e   @jsonProperty\n\u003e   public readonly name: string\n\u003e }\n\u003e ```\n\nTo decode an `Account` class, use `JsonDecoder.decode` with a JSON object  (or JSON string) and the decodable class to decode, in this case `Account`\n\n\u003e ```TypeScript\n\u003e import { JsonDecoder } from 'json-decoder'\n\u003e import { Account } from './account'\n\u003e\n\u003e const json = {\n\u003e   id: 1001,\n\u003e   name: 'John Smith',\n\u003e }\n\u003e // Decode the above JSON for 'Account' class\n\u003e const account = JsonDecoder.decode\u003cAccount\u003e(json, Account)\n\u003e ```\n\n## Property Aliases\n\nThe class you are authoring and the JSON used as a source might not be a one to one match. You class may want to use different property names or even structure the representation different to the source JSON.\n\nThis is where \"aliases\" come in and the `jsonPropertyAlias` comes into play.\n\nThe following uses the decorator describing the JSON property `accountId` mapped to the class prototype property `id`.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonPropertyAlias } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // Alias 'id' to 'accountId' property\n\u003e   @jsonPropertyAlias('account-id')\n\u003e   public readonly id: number\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n\nNote the JSON object below is using `account-id` instead of `id`\n\n\u003e ```TypeScript\n\u003e import { JsonDecoder } from 'json-decoder'\n\u003e import { Account } from './account'\n\u003e\n\u003e const json = {\n\u003e   'account-id': 1001,\n\u003e   name: 'John Smith',\n\u003e }\n\u003e const account = JsonDecoder.decode\u003cAccount\u003e(json, Account)\n\u003e ```\n\nThe decoder will automatically map the `id` value based on the alias given in the `@jsonPropertyAlias`.\n\n### Property key paths\n\nSimple property renaming does not help with restructuring the original JSON. The decoder can help here too with **Key Paths** alias. Key paths are dot/@-notion paths to JSON values.\n\nIf the desire was to flatten a JSON object's properties this can be achieved by using a path similar to that used in TypeScript/JavaScript itself\n\n\u003e ```TypeScript\n\u003e {\n\u003e   id: 1001,\n\u003e   name: {\n\u003e     title: 'Dr',\n\u003e     fullName: 'John Smith',\n\u003e     suffix: 'III',\n\u003e   }\n\u003e }\n\u003e ```\n\nThe of interest `name` properties can be flatten\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonPropertyAlias } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   @jsonPropertyAlias('name.title')\n\u003e   public readonly title: string\n\u003e\n\u003e   @jsonPropertyAlias('name.fullName')\n\u003e   public readonly fullName: string\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n\nThe `@`-notation can be used to address indexes of arrays if needed.\n\n\u003e ```TypeScript\n\u003e {\n\u003e   id: 1001,\n\u003e   name: {\n\u003e     title: 'Dr',\n\u003e     fullName: 'John Smith',\n\u003e     suffix: 'III',\n\u003e   },\n\u003e   profiles: [2001, 2002, 2451],\n\u003e }\n\u003e ```\n\nThe decode could map index `0` of `profiles` by using `profiles@0`. At the same time map all the profiles from the JSON.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonPropertyAlias, jsonProperty } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // ...\n\u003e\n\u003e   @jsonPropertyAlias('profiles@0')\n\u003e   public readonly primaryProfile: number\n\u003e\n\u003e   @jsonProperty\n\u003e   public readonly profiles: Array\u003cnumber\u003e\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n\nKey paths can be a long as the JSON supports. `property.arr@0.anotherProperty.YAP` will attempt to look up `YAP`, the end value. If at any point any sub-property returns `undefined` then `undefined` will be returned. If `null` is returned for any but the last property then `undefined` will be returned, else `null` will be returned.\n\n`@` and `.` are synonymous with one another (subject to change) but provide a more readable structure of intent. That is '.' sub-property, '@' index. `a.b.c` is the same as `a@b@c`.\n\n### Root Indexing\n\nAs a convenience, you may use `@index` as an alias, which will attempt to map a source JSON object that is an array, at `index` to the class prototype property.\n\nUsing an example of a message protocol from a network request, the message is composed of a `[header, body]` structure.\n\n\u003e ```TypeScript\n\u003e [\n\u003e   { message: 'ACCOUNT' },\n\u003e   { name: 'John Smith' },\n\u003e ]\n\u003e ```\n\nDecoding the above message use `@jsonPropertyAlias('@1.name')`, where `@1` represents the body of the message.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonPropertyAlias } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class AccountMessage {\n\u003e   // ...\n\u003e\n\u003e   @jsonPropertyAlias('@1.name')\n\u003e   public readonly name: string\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n\n## Type Marshalling\n\nType marshalling is a built-in feature of the decoder. Type marshalling allows a property of one type to be automatically converted into another. This allows more flexibility and implicit type conversion to happen automatically.\n\nThe `@jsonType` decorator allows the declaration of the class to marshal the property value to.  Natively supported types are:\n* Array\n* Boolean\n* Date (as timestamp or string)\n* Map\n* Number\n* Set\n* URL\n\nIf class passed to `@jsonType()` is _also_ a `@jsonDecodable` class, then `json-decoder` will attempt to deserialize the object associated with the current `@jsonProperty` using the specified class.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonType } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // Use 'Number' marshalling\n\u003e   @jsonProperty\n\u003e   @jsonType(Number)\n\u003e   public readonly id: number\n\u003e }\n\u003e ```\n\nThe above will attempt to convert a JSON property value of `id` into a `Number`. The marshalling attempts to perform a smart conversion based on the property in the JSON. For `Number`s this could be Integers or Floats, and supports Base10, Base16 (Hex) and Base2 (Binary) number strings similar to JavaScript notation.\n\n\u003e ```TypeScript\n\u003e import { JsonDecoder } from 'json-decoder'\n\u003e import { Account } from './account'\n\u003e\n\u003e const json = {\n\u003e   id: '0x3E9',\n\u003e   name: 'John Smith',\n\u003e }\n\u003e const account = JsonDecoder.decode\u003cAccount\u003e(json, Account)\n\u003e ```\n\n### Collection Marshalling\n\n`jsonType` can also be used to marshal iterable collections such as `Array`, `Map` and `Set`.  To specify a collection with a specific type of element,  `@jsonType({ collection: Array | Map | Set, element: Type })` (an array of one type element).\n\n```javascript\n{\n  id: 1001,\n  profiles: ['2001', '2002', '2451'],\n}\n```\nTo marshal `profiles` into an `Array` of `Number`\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonType } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // ...\n\u003e\n\u003e   @jsonType({ collection: Array, element: Number })\n\u003e   public readonly profiles: Array\u003cnumber\u003e\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n`element` types can also be nested collections. For example:\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonType } from './json-decoder'\n\u003e\n\u003e @jsonDecodable()\n\u003e class Account {\n\u003e   // ...\n\u003e\n\u003e   @jsonType({ collection: Map, element: { collection: Map, element: String }})\n\u003e   public readonly nestedMap: Map\u003cstring, Map\u003cstring, string\u003e\u003e\n\u003e\n\u003e   // ...\n\u003e }\n\u003e ```\n\n\nArray marshalling can also convert a single property value into an array. If the JSON object only had a single scalar property value instead, it will creating an `Array` of one element.\n\nAll element marshaling still apply here too\n\n```javascript\n{\n  id: 1001,\n  profiles: '2001',\n}\n```\n\n### Reverse Array Marshaling\n\nWith marshaling arrays, a JSON property with an array can be marshalled into a single property value. In this case only the first element of the array is taken.\n\n\u003e ```javascript\n\u003e {\n\u003e   id: 1001,\n\u003e   profiles: ['2001', '2002', '2451'],\n\u003e }\n\u003e ```\n\n\u003e ```Javascript\n\u003e   // Marshal `profiles` into a single value\n\u003e   @jsonType(Number)\n\u003e   public readonly primaryProfile: number\n\u003e\n\u003e   // ...\n\u003e\n\u003e   @jsonType({ collection: Array, element: Number })\n\u003e   public readonly profiles: Array\u003cnumber\u003e\n\u003e ```\n\nWhen decoded `primaryProfile` will be set to `2001` as a `Number`\n\n\n### Custom Marshaling\nSometimes when marshaling a given attribute, one may wish to apply custom business logic to calculate the value that is assigned to the `@jsonProperty`.  To accomplish this, a function may be optionally passed as a second parameter to `@jsonType` with a signature of:\n\n\u003e```TypeScript\n\u003e (value: Type): OtherType\n\u003e```\nFor example, if a source JSON document contains an unbounded numeric value but once deserialized, it should be limited to a maximum decimal value:\n\u003e ```javascript\n\u003e {\n\u003e   id: 1001,\n\u003e   size: 9999,\n\u003e }\n\u003e ```\n\n\u003e ```TypeScript\n\u003e   const MAX_VALUE = 1000.01\n\u003e\n\u003e   @jsonType(Number)\n\u003e   public readonly id: number\n\u003e\n\u003e   @jsonPropertyAlias('size')\n\u003e   @jsonType(Number, (data: number): number =\u003e {\n\u003e       return Math.min(MAX_VALUE, data)\n\u003e   })\n\u003e   public readonly decimalSize: Number\n\u003e ```\n\n## Custom Constructors\n### `@jsonDecodable({ useConstructor: true })`\nSometimes when instantiating a `@jsonDecodable` class, it becomes necessary to execute some custom initialization code in the class constructor.  However, `json-decoder` will _not_ use the constructor unless directed to do so.  To cause the class constructor to get invoked when `decode()` is called, pass `{ useConstructor: true }` to `@jsonDecodable`.\n\u003e```TypeScript\n\u003e import { jsonDecodable, jsonType } from './json-decoder'\n\u003e\n\u003e @jsonDecodable({ useConstructor: true })\n\u003e class Account {\n\u003e    constructor() {\n\u003e       super()\n\u003e      // do custom initialization\n\u003e    }\n\u003e\n\u003e   @jsonProperty\n\u003e   @jsonType(Number)\n\u003e   public readonly id: number\n\u003e }\n\u003e```\n\n### `jsonDecoderFactory`\nSimilarly, if one is using inheritance to describe `@jsonDecodable` classes, it may be desirable to call `decode` on a base class (which may be `abstract`), and dynamically instantiate a derived class based on the contents of the JSON.\n\n\u003e```TypeScript\n\u003e@jsonDecodable({ useConstructor: true })\n\u003eabstract class BaseClass {\n\u003e\n\u003e    @jsonProperty\n\u003e    name: string\n\u003e\n\u003e    @jsonProperty\n\u003e    id: number\n\u003e\n\u003e    @jsonDecoderFactory\n\u003e    static decoderClassFactory(json: JsonObject): DerivedClass_1 | DerivedClass_2 {\n\u003e        return json['some-attribute'] === 'some value'\n\u003e            ? new DerivedClass_1()\n\u003e            : new DerivedClass_2()\n\u003e    }\n\u003e\n\u003e    abstract toJSON(): JsonObject\n\u003e}\n\u003e```\n## JSON Schema Validation\n`json-decoder` supports validation of JSON documents using [JSON Schema](https://ajv.js.org/json-schema.html).  To enable schema validation, add the `@jsonSchema()` decorator to your class definition.\n\n\u003e ```TypeScript\n\u003e import { jsonDecodable, jsonType } from './json-decoder'\n\u003e\n\u003einterface MyData {\n\u003e  foo: number\n\u003e  bar?: string\n\u003e}\n\u003e\n\u003econst schema: JSONSchemaType\u003cMyData\u003e = {\n\u003e    $id: \"http://schemas.example.com/my-data\",\n\u003e    $schema: \"http://json-schema.org/draft-07/schema#\",\n\u003e    description: \"This is my data.\",\n\u003e    type: \"object\",\n\u003e    properties: {\n\u003e        foo: {type: \"integer\"},\n\u003e        bar: {type: \"string\", nullable: true}\n\u003e    },\n\u003e    required: [\"foo\"],\n\u003e    additionalProperties: false\n\u003e}\n\u003e\n\u003e @jsonDecodable({ useConstructor: true })\n\u003e @jsonSchema(schema)\n\u003e class Account {\n\u003e   // Use 'Number' marshalling\n\u003e   @jsonProperty\n\u003e   public readonly foo: number\n\u003e\n\u003e   @jsonProperty\n\u003e   bar?: string\n\u003e }\n\u003e ```\nWhen `decode()` is called, the schema validation will be executed automatically.  If validation fails for any reason an `Error` will be thrown containing each of the validation errors encountered.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhappycodelucky%2Fjson-decoder-node","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhappycodelucky%2Fjson-decoder-node","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhappycodelucky%2Fjson-decoder-node/lists"}