Package Exports
- typescript-json-serializer
- typescript-json-serializer/dist/index.cjs.js
- typescript-json-serializer/dist/index.esm.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (typescript-json-serializer) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
typescript-json-serializer
A typescript library to deserialize json into typescript classes and serialize classes into json.
Summary
Installation
npm install typescript-json-serializer --save
# or
yarn add typescript-json-serializer
You also need to set experimentalDecorators and emitDecoratorMetadata to true into the tsconfig.json file.
For example:
{
"compilerOptions": {
...
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
...
}
}
Usage
import { JsonSerializer, throwError } from 'typescript-json-serializer';
import { json } from '../json/data';
import { Organization } from '../models/organization';
// Instantiate a default serializer
const defaultSerializer = new JsonSerializer();
// Or you can instantiate a serializer with your custom options
const customSerializer = new JsonSerializer({
// Throw errors instead of logging
errorCallback: throwError,
// Allow all nullish values
nullishPolicy: {
undefined: 'allow',
null: 'allow'
};
// e.g. if all the properties in the json object are prefixed by '_'
formatPropertyName: (propertyName: string) => `_${propertyName}`;
})
// Deserialize
const organization = defaultSerializer.deserialize(json, Organization);
// Serialize
const data = defaultSerializer.serialize(organization);
Examples
Classes
// Import decorators from library
import { JsonObject, JsonProperty } from 'typescript-json-serializer';
// Enums
export enum Gender {
Female,
Male,
Other
}
export enum Status {
Alive = 'Alive',
Sick = 'Sick',
DeadAndAlive = 'Dead and alive',
Dead = 'Dead'
}
// Create a JsonObject class: LivingBeing
// JsonObject decorator
@JsonObject()
export class LivingBeing {
/** The living being id (PK) */
@JsonProperty() id: number;
}
// Create a JsonObject class that extends LivingBeing: Human
@JsonObject()
export class Human extends LivingBeing {
constructor(
// This comment works
// Override LivingBeing id property name
// and set required to true
@JsonProperty({name: 'humanId', required: true})
public name: string,
public id: number,
@JsonProperty() public gender: Gender,
/** This comment works */
@JsonProperty() public readonly birthDate: Date
) {
super();
this.id = id;
}
}
// Create a JsonObject class: PhoneNumber
@JsonObject()
export class PhoneNumber {
@JsonProperty() countryCode: string;
@JsonProperty() value: string;
}
// Create a JsonObject class that extends Human: Employee
@JsonObject()
export class Employee extends Human {
/** The employee's email */
@JsonProperty({required: true}) email: string;
/** Predicate function to determine if the property type
* is PhoneNumber or a primitive type */
@JsonProperty({
type: property => {
if (property && property.value !== undefined) {
return PhoneNumber;
}
}
})
phoneNumber: PhoneNumber | string;
constructor(
public name: string,
// Override human id property name
// (keep the require to true from Human id)
@JsonProperty('employeeId') public id: number,
public gender: Gender,
public birthDate: Date
) {
super(name, id, gender, birthDate);
}
}
// Create a JsonObject class: Animal
@JsonObject()
export class Animal {
@JsonProperty() id: number;
@JsonProperty() name: string;
@JsonProperty() birthDate: Date;
@JsonProperty() numberOfPaws: number;
@JsonProperty() gender: Gender;
// Enum value (string)
@JsonProperty() status: Status;
// Specify the property name of json property if needed
@JsonProperty('childrenIdentifiers')
childrenIds: Array<number>;
constructor(name: string) {
this.name = name;
}
}
// Create a JsonObject class that extends Animal (which extends LivingBeing): Panther
@JsonObject()
export class Panther extends Animal {
@JsonProperty() color: string;
// JsonProperty directly inside the constructor
// for property parameters
public constructor(
name: string,
@JsonProperty() public isSpeckled: boolean
) {
super(name);
}
}
// Create a JsonObject class that extends Animal
// (which extends LivingBeing): Snake
@JsonObject()
export class Snake extends Animal {
@JsonProperty() isPoisonous: boolean;
public constructor(args: { name: string; isPoisonous: boolean }) {
super(args.name);
this.isPoisonous = args.isPoisonous;
}
}
// Create a JsonObject empty class that extends Animal
// (which extends LivingBeing): UnknownAnimal
@JsonObject()
export class UnknownAnimal extends Animal {
public constructor(name: string) {
super(name);
}
}
// Create a JsonObject class: Zoo
// Function to transform coordinates into an array
const coordinatesToArray = (coordinates: {
x: number;
y: number;
z: number;
}): Array<number> => {
return Object.values(coordinates);
};
// Function to transform an array into coordinates
const arrayToCoordinates = (array: Array<number>): {
x: number;
y: number;
z: number
} => {
return {
x: array[0],
y: array[1],
z: array[2]
};
};
// A predicate function use to determine what is the
// right type of the data (Snake or Panther)
const snakeOrPanther = animal => {
return animal && animal['isPoisonous'] !== undefined
? Snake
: Panther;
};
@JsonObject()
export class Zoo {
// Here you do not need to specify the type
// inside the decorator
@JsonProperty() boss: Employee;
@JsonProperty() city: string;
@JsonProperty() country: string;
// Property with transform functions executed respectively
// on serialize and on deserialize
@JsonProperty({
beforeDeserialize: arrayToCoordinates,
afterSerialize: coordinatesToArray
})
coordinates: { x: number; y: number; z: number };
// Array of none-basic type elements
@JsonProperty({ type: Employee })
employees: Array<Employee>;
@JsonProperty() id: number;
@JsonProperty() name: string;
// Array of none-basic type elements where you need to
// specify the name of the json property
// and use the predicate function to cast the deserialized
// object into the correct child class
@JsonProperty({ name: 'Animals', type: snakeOrPanther })
animals: Array<Animal>;
// Property that can be Panther or Snake type
// Use again the predicate function
@JsonProperty({ type: snakeOrPanther })
mascot: Panther | Snake;
// Dictionary of empty child classes
@JsonProperty({ isDictionary: true, type: UnknownAnimal })
unknownAnimals: { [id: string]: UnknownAnimal };
// Dictionary of PhoneNumber or string
@JsonProperty({
isDictionary: true,
type: property => {
if (property && property.value !== undefined) {
return PhoneNumber;
}
}
})
phoneBook: { [id: string]: PhoneNumber | string };
// Property which will be not serialized and deserialized
// but event accessible and editable from Zoo class.
public isFree: boolean = true;
public constructor() { }
}
// Create a JsonObject class that extends Society: Organization
@JsonObject()
export class Organization extends Society {
@JsonProperty({ type: Zoo }) zoos: Array<Zoo>;
@JsonProperty({ isDictionary: true })
zoosName: { [id: string]: string };
// To merge multiple properties in a single one
// use the property `names`.
// If you don't create your own merge with the `beforeDeserialize`
// and `afterSerialize` function, it will just merge properties
// in this one when using `deserialize` and split back
// when using `serialize`
@JsonProperty({
name: [
'mainShareholder',
'secondaryShareholder',
'thirdShareholder'
],
type: Human,
beforeDeserialize: value => Object.values(value),
afterSerialize: value => {
return {
mainShareholder: value[0],
secondaryShareholder: value[1],
thirdShareholder: value[2]
};
}
})
shareholders: Array<Human>;
}
// Create a JsonObject class: Society
@JsonObject()
export class Organization {
@JsonProperty() id: string;
@JsonProperty() name: string;
}
Json data
// data.ts
export const data: any = {
id: '1',
name: 'Zoos Organization',
zoosName: {
'15': 'The Greatest Zoo',
'16': 'Zoo Zoo'
},
zoos: [
{
id: 15,
name: 'The Greatest Zoo',
city: 'Bordeaux',
coordinates: [1, 2, 3],
country: 'France',
boss: {
employeeId: 1,
name: 'Bob Razowsky',
birthDate: '1984-04-03T22:00:00.000Z',
email: 'bob.razowsky@tgzoo.fr',
gender: 1,
phoneNumber: '111-111-1111'
},
employees: [
{
employeeId: 1,
name: 'Bob Razowsky',
birthDate: '1984-04-03T22:00:00.000Z',
email: 'bob.razowsky@tgzoo.fr',
gender: 1,
phoneNumber: '111-111-1111'
},
{
employeeId: 2,
name: 'Mikasa Ackerman',
birthDate: '1984-01-11T22:00:00.000Z',
email: 'mikasa.ackerman@tgzoo.fr',
gender: 0,
phoneNumber: '222-222-2222'
},
{
employeeId: 3,
name: 'Red Redington',
birthDate: '1970-12-04T22:00:00.000Z',
email: 'red.redington@tgzoo.fr',
gender: 1,
phoneNumber: '333-333-3333'
},
{
employeeId: 4,
name: 'Fried Richter',
birthDate: '1994-04-01T22:00:00.000Z',
email: 'fried.richter@tgzoo.fr',
gender: 1
}
],
Animals: [
{
id: 1,
name: 'Bagheera',
birthDate: '2010-01-11T22:00:00.000Z',
numberOfPaws: 4,
gender: 1,
childrenIdentifiers: [2, 3],
color: 'black',
isSpeckled: false,
status: 'Sick'
},
{
id: 2,
name: 'Jolene',
birthDate: '2017-03-10T22:00:00.000Z',
numberOfPaws: 4,
gender: 0,
color: 'blond',
isSpeckled: true,
status: 'Alive'
},
{
id: 3,
name: 'Ka',
birthDate: '2018-09-09T00:00:00.000Z',
numberOfPaws: 0,
gender: 1,
isPoisonous: true
},
{
id: 4,
name: 'Schrodinger',
numberOfPaws: 4,
gender: 1,
color: 'brown',
isSpeckled: false,
status: 'Dead and alive'
}
],
mascot: {
id: 1,
name: 'Bagheera',
birthDate: '2010-01-11T22:00:00.000Z',
numberOfPaws: 4,
gender: 1,
childrenIdentifiers: [2, 3],
color: 'black',
isSpeckled: false,
status: 'Sick'
},
unknownAnimals: {
'1': {
name: null
}
},
phoneBook: {
'1': {
value: '111-111-1111'
},
'2': {
value: '222-222-2222'
},
'3': '333-333-3333'
}
},
{
id: 16,
name: 'Zoo Zoo',
city: 'Paris',
coordinates: [4, 2, 3],
country: 'France',
boss: {
employeeId: 2,
name: 'Sully',
birthDate: '1984-08-03T22:00:00.000Z',
email: 'sully.razowsky@tgzoo.fr',
gender: 1,
phoneNumber: {
countryCode: '33',
value: '0111111111'
}
},
employees: [],
Animals: [],
mascot: null,
unknownAnimals: {}
}
],
mainShareholder: {
humanId: 100,
name: 'Elon Musk',
birthDate: '1971-06-28T22:00:00.000Z',
gender: 1
},
secondaryShareholder: null
};
API
Decorators
@JsonObject()
Used to make a class serializable.
Example
@JsonObject()
class MyClass {}
@JsonProperty()
Used to make a class property serializable, property will be ignored if not set.
Parameters
options
Type: string
| JsonPropertyOptions
Optional: true
Description: The option to customize the serialization/deserialization of the target property.
Example
@JsonProperty(options) myProperty: string;
JsonSerializer
Constructor
constructor(options?: Partial<JsonSerializerOptions>) {}
Parameters
options
Type: Partial<JsonSerializerOptions>
Optional: true
Description: The options to customize the serializer.
Properties
options
Type: Partial<JsonSerializerOptions>
Optional: false
Description: The options to customize the serializer.
Default value:
{
errorCallback: logError,
nullishPolicy: {
undefined: 'remove',
null: 'allow'
}
}
Methods
deserialize()
To use when you don't know if the value to deserialize is an object or an array.
deserialize<T extends object>(
value: string | object | Array<object>,
type: Type<T> | T
): T | Array<T|Nullish> | Nullish
Parameters
value
Type: string
| object
| Array<object>
Optional: false
Description: The value to deserialize.
type
Type: Type<T>
| T
Optional: false
Description: The constructor class to deserialize into.
Return
T
or Array
or Nullish
deserializeObject()
To use when the value to deserialize is an object.
deserializeObject<T extends object>(
obj: string | object,
type: Type<T> | T
): T | Nullish
Parameters
obj
Type: string
| object
Optional: false
Description: The object to deserialize.
type
Type: Type<T>
| T
Optional: false
Description: The constructor class to deserialize into.
Return
T
or Nullish
deserializeObjectArray()
To use when the value to deserialize is an array.
deserializeObjectArray<T extends object>(
array: string | Array<any>,
type: Type<T> | T
): Array<T|Nullish> | Nullish
Parameters
array
Type: string
| Array<any>
Optional: false
Description: The object to deserialize.
type
Type: Type<T>
| T
Optional: false
Description: The constructor class to deserialize into.
Return
Array
or Nullish
serialize()
To use when you don't know if the value to serialize is an object or an array
serialize(value: object | Array<object>): object | Array<object|Nullish> | Nullish
Parameters
value
Type: object
| Array<object>
Optional: false
Description: The object or the array of objects to serialize.
Return
object
or Array
or Nullish
serializeObject()
To use when the value to serialize is an object.
serializeObject(instance: object): object | Nullish
Parameters
instance
Type: object
Optional: false
Description: The object to serialize.
Return
object
or Nullish
serializeObjectArray()
To use when the value to serialize is an array of objects.
serializeObjectArray(array: Array<object>): Array<object|Nullish> | Nullish
Parameters
array
Type: Array<object>
Optional: false
Description: The array of objects to serialize.
Return
Array
or Nullish
Definitions
Types
JsonPropertyOptions
name?: string | Array<string>;
type?: Function | PredicateProto;
isDictionary?: boolean;
required?: boolean;
beforeSerialize?: IOProto;
afterSerialize?: IOProto;
beforeDeserialize?: IOProto;
afterDeserialize?: IOProto;
JsonSerializerOptions
errorCallback?: ErrorCallback = logError;
nullishPolicy: NullishPolicy = {
undefined: 'remove',
null: 'allow'
};
formatPropertyName?: FormatPropertyNameProto;
NullishPolicy
undefined: Policy;
null: Policy;
Value types
Nullish
null | undefined
Policy
'allow' | 'disallow' | 'remove'
Functions types
ErrorCallback
(message: string) => void
The library provide two built-in methods:
logError
that logs the error.throwError
that throws the error.
FormatPropertyNameProto
(propertyName: string) => string;
IOProto
(property: any, currentInstance?: any) => any
PredicateProto
(property: any, parentProperty?: any) => any
Type<T>
new (...args: Array<any>) => T;
Note: represent a constructor
.
Development
Prerequisites
- NodeJS: https://nodejs.org
- Yarn: https://yarnpkg.com
Install dependencies
yarn
Run build
yarn build
Run linter
yarn lint
Run tests
yarn test
Thanks to
Author
Gillian Pérard - @GillianPerard
Contributors
- Hyeonsoo David Lee - @civilizeddev