ts-jackson
A typescript library to deserialize and serialize json into classes and vice versa. You can use different path pattern
to resolve deeply nested structures. Every path pattern provided by lodash.set
function is supported.
Check out src/examples as a reference.
Installation:
npm install ts-jackson --save
yarn add ts-jackson
For tsconfig.json
file set experimentalDecorators
and emitDecoratorMetadata
to true
to allow
decorators support:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
Api:
Imports:
import { JsonProperty, Serializable, deserialize, serialize, SerializableEntity } from 'typescript-json-serializer';
Decorators
Serializable
To mark your class as serializable wrap your class with Serializable decorator:
@Serializable()
class Class {}
JsonProperty
declare type Params<P> = {
path?: string
paths?: string[]
required?: boolean
type?:
| (new (...args: any[]) => P)
| {
[K in keyof P]: new (...args: any[]) => P[K]
}
elementType?: new (...args: any[]) => P extends [] ? P[0] : any
validate?: (property: P) => boolean
deserialize?: (jsonValue: any) => P
serialize?: (property: P) => any
afterDeserialize?: (
deserializedInstance: InstanceType<new (...args: any[]) => any>,
propertyValue: any
) => P
beforeSerialize?: (propertyValue: P) => any
afterSerialize?: (serializedData: any) => any
}
export default function JsonProperty<P = unknown>(
arg?: Params<P> | string | string[]
): (object: Object, propertyName: string) => void
Path:
The path property can be set in a few different ways:
class Cat {
@JsonProperty()
name: string
}
class Track {
@JsonProperty('duration_ms')
durationMs: number
}
class Track {
@JsonProperty({
path: 'duration_ms'
})
durationMs: number
}
Path property supports different formats for resolving deeply nested structures provided by lodash
_.set(object, path, value)
Multiple paths
You can resolve property as a combination of multiple json paths, provided either as Params
property or
as an argument to @JsonProperty
decorator:
const json = {
images: {
smallImage: {
url: 'mediumImageUrl',
},
mediumImage: {
url: 'mediumImageUrl',
},
bigImage: {
url: 'bigImageUrl',
},
},
}
@Serializable()
class Playlist {
@JsonProperty({
paths: ['images.smallImage', 'images.mediumImage', 'images.bigImage'],
elementType: Image,
})
images: Image[]
}
You can also provide custom deserialize, beforeSerialize functions:
const json = {
images: [
{
url: 'mediumImageUrl',
},
{
url: 'mediumImageUrl',
},
{
url: 'bigImageUrl',
},
],
}
@Serializable()
class Playlist {
@JsonProperty({
paths: ['images[0]', 'images[2]'],
elementType: Image,
deserialize: ([icon, cover]: Image[]) => ({ icon, cover }),
beforeSerialize: (images) => [images.icon, images.cover],
})
images: {
icon: Image
cover: Image
}
}
Resolving deeply nested structures:
const trackJson = {
track: {
id: 'some id',
},
}
@Serializable()
class Track {
@JsonProperty('track.id')
readonly id: string
}
const deserialized = deserialize(trackJson, Track)
serialize(deserialized)
Resolving array structures
const jsonData = {
images: {
items: [
{
height: 300,
url:
'https://i.scdn.co/image/ab67616d0000b27380368f0aa8f90c51674f9dd2',
width: 300,
},
{
height: 640,
url:
'https://i.scdn.co/image/ab67616d00001e0280368f0aa8f90c51674f9dd2',
width: 640,
},
],
},
}
@Serializable()
class Playlist {
@JsonProperty('images.items[1]')
readonly backgroundImage: Image
}
const deserialized = deserialize(jsonData, Playlist)
const serialized = serialize(deserialized)
For more patterns for resolving structures check out lodash/get docs.
deserialize
export default function deserialize<T, U extends Array<unknown>>(
json: Record<string, unknown> | string,
serializableClass: new (...args: [...U]) => T,
...args: U
): T
serialize
export default function serialize<
T extends new (...args: unknown[]) => unknown
>(instance: InstanceType<T>): Record<string, unknown> {
Examples
const trackJson = {
track: {
id: 'some id',
},
}
@Serializable()
class Track {
@JsonProperty('track.id')
readonly id: string
}
const deserializedClassIntance = deserialize(trackJson, Track)
const serializedJson = serialize(deserializedClassIntance)
Using constructor arguments:
@Serializable()
class Image {
@JsonProperty({ required: true })
readonly url: string
constructor(readonly width: number, readonly height: number) {
}
}
deserialize({url: 'url'}, Image, 5, 3)
const deserializedClassIntance = deserialize({url: 'some url'}, Image, 5, 4)
const serializedJson = serialize(deserializedClassIntance)
SerializableEntity
export default class SerializableEntity {
stringify(): string
serialize(): Record<string, unknown>
static deserialize<T, U extends Array<unknown>>(
this: {
new (...params: [...U]): T
},
json: Record<string, unknown>,
...args: U
): T
}
class Image extends SerializableEntity {
@JsonProperty()
readonly height?: number
@JsonProperty()
readonly width?: number
@JsonProperty({ required: true })
readonly url: string
}
const imageJson = {
height: '234',
width: '123',
url: 'http://localhost:8080',
}
const image = Image.deserialize(imageJson)
image.serialize()
image.stringify()