JS library for serializing and deserializing complex objects into JSON and back.
-
two-way serialization / deserialization without specifying target type - thanks to
@type
property -
defining exposed / excluded properties via groups or graph
-
defining custom serialization / deserialization functions (per type or per property)
-
built-in support for array of objects and
Record<string, T>
maps -
ESM
-
zero dependencies
-
full TypeScript support
-
covered with tests
yarn add serialzr
You can control which properties are included in the serialized output and which are not.
This can be done using the @Srlz.Expose()
and @Srlz.Exclude()
decorators.
class Foo
{
@Srlz.Expose()
public exposedProperty : string = 'exposed';
@Srlz.Exclude()
public excludedProperty : string = 'excluded';
}
const plain = serializer.toPlain(new Foo());
// { exposedProperty: 'exposed' }
You can specify groups for properties, allowing you to control the serialization and deserialization process based on these groups.
class Foo
{
@Srlz.Expose([ 'group1' ])
public property1 : string = 'property1';
@Srlz.Expose([ 'group2' ])
public property2 : string = 'property2';
}
const plain = serializer.toPlain(new Foo(), {
groups: [ 'group1' ]
});
// { property1: 'property1' }
The library allows you to automatically add groups based on certain conditions.
@Srlz.AutoGroup('detailed', (obj, ctx) => ctx.detailedEntries?.includes(obj.id))
class Entry
{
@Srlz.Id()
public id : number = 1;
@Srlz.Expose([ 'detailed' ])
public stats : string = 'stats';
}
const plain = serializer.toPlain(new User(), {
ctxData: {
detailedEntries: [ 1, 2 ]
}
});
// { id: 1, stats: 'stats' }
The library supports graph serialization, allowing you to control how nested objects are serialized.
@Srlz.Type('book')
class Book
{
@Srlz.Id()
public id : number = 5;
@Srlz.Expose([ 'public' ])
public name : string = 'Noname';
public constructor (data : Partial<Book> = {})
{
Object.assign(this, data);
}
}
@Srlz.Type('author')
class Author
{
@Srlz.Id()
public id : number = 5;
@Srlz.Expose([ 'public' ])
public name : string = 'John Doe';
@Srlz.Expose([ 'public' ])
public age : number = 18;
@Srlz.Type({ arrayOf: () => Book })
public books : Book[] = [
new Book({ id: 8, name: 'Book 1' }),
new Book({ id: 9, name: 'Book 2' }),
];
@Srlz.Expose([ 'owner' ]) // only owner can see this
@Srlz.Type({ recordOf: () => Book })
public aliasedBooks : Record<string, Book> = {
a: new Book({ id: 10, name: 'Book 3' }),
};
@Srlz.Exclude() // excluded even if default strategy is to include
public secret : number = 12345;
public constructor (data : Partial<Author> = {})
{
Object.assign(this, data);
}
}
const plain = serializer.toPlain(new Author(), {
graph: {
$default: false,
id: true,
name: true,
books: '**',
aliasedBooks: {
id: true,
name: true,
}
}
});
// {
// id: 5,
// name: 'John Doe',
// books: [
// { id: 8, name: 'Book 1' },
// { id: 9, name: 'Book 2' }
// ],
// aliasedBooks: {
// a: { id: 10, name: 'Book 3' }
// }
// }
You can define custom strategies for serializing and deserializing properties.
@Srlz.Transformer({
toPlain: (obj) => obj.text.toLowerCase(),
toClass: (plain) => new Tag({ text: plain.toLowerCase() }),
})
class Tag
{
public text : string = 'tag';
public constructor (data : Partial<Tag> = {})
{
Object.assign(this, data);
}
}
As class decorator, it marks the class as a serializable type.
function Type (typeName : string)
function Type (typeDef : {
name? : string,
idProperty? : string,
})
Defines a group that is automatically added to the object based on the condition.
function AutoGroup (
groupName : string, // group name
condition : (
obj : any, // currently serialized object
ctx : any // context data (passed to toPlain and toClass methods under ctxData key)
) => boolean
)
Defines custom serialization and deserialization strategies for the class.
type TransformerFnParams = {
direction : Direction,
type : any,
options? : SerializationOptions.Base<T> | any,
context? : SerializationContext.Base<T> | any,
}
function Transformer (
def : {
toPlain : (obj : any, params : TransformerFnParams) => any, // serialization function (before exposure calculation)
toClass : (plain : any, params : TransformerFnParams) => any, // deserialization function (before exposure calculation)
}
)
function Transformer (
def : {
toPlain : {
before? : (obj : any, params : TransformerFnParams) => any, // before exposure calculation
after? : (plain : any, params : TransformerFnParams) => any, // after exposure calculation
}
toClass : {
before? : (plain : any, params : TransformerFnParams) => any, // before exposure calculation
after? : (obj : any, params : TransformerFnParams) => any, // after exposure calculation
}
}
)
Marks the property as an identifier.
Marks the property as exposed.
function Expose () // exposes property by default
function Expose (groups : string[]) // exposes property only for specified groups
function Expose (def : { // complex configuration
// modifiers
mode? : boolean = true, // true - expose, false - exclude
deeply? : boolean, // expose deeply
// conditions
all? : string[], // matched if all groups are present
any? : string[], // matched if any group is present
notAll? : string[], // matched if no all groups are present
notAny? : string[], // matched if none of groups is present
})
Marks the property as excluded.
Sample usage is the same as for @Srlz.Expose()
.
Defines the type of the property.
It is required to specify the type of the property if it is not a primitive type.
function Type (type : () => ClassConstructor) // for single object
function Type (def : {
type : () => ClassConstructor, // for single object
arrayOf : () => ClassConstructor, // for array of objects
recordOf : () => ClassConstructor, // for record of objects
})
Defines custom serialization and deserialization strategies for the property.
Usage is the same as for @Srlz.Transformer()
class decorator.
Defines a computed property.
type GetterFn = (args : {
value : any, // current property value
parent : any, // parent object
params : TransformerFnParams,
}) => any;
function Computed (getterFn : GetterFn)
Defines a computed property.
function ComputedByGroup (
group : string, // group name to match
ifIncludes : GetterFn = () => true, // getter function to use if group is included
ifNotIncludes : GetterFn = () => false, // getter function to use if group is not included
)