@cljimenez/json-serializer-core
v1.0.2
Published
Wrapper to use JSON.parse and JSON.stringify methods, based on serializers
Downloads
10
Maintainers
Readme
Wrapper to use JSON.parse and JSON.stringify methods, based on serializers
🏠 Homepage
Index
Install
npm install @cljimenez/json-serializer-core
If you want to use some base serializers, you can use @cljimenez/json-serializer-base-serializers
What is a Serializer?
A Serializer is an object that contains three methods:
getSerializerType(void)=>string: Returns the type of serializer, for example: A serializer that serializes
functions
(FunctionSerializer) must return the string'function'
. The type returned by this method can be one of the following:Some value returned by typeof operator:
'array'
,'function'
,'bigint'
,'string'
,'object'
, etc.Example:
const myFunction = (arg1, arg2) => `${arg1} and ${arg2}` // Create the serializer const functionSerializer = { getSerializerType: () => 'function', serialize: (unserializedData) => { /* returns serialized data */ }, parse: (serializedData) => { /* returns unserialized data */ }, } /* Using class */ class FunctionSerializer { /* you can to define constructor, other methods, etc */ getSerializerType() { return 'function' } serialize(unserializedData) { /* returns serialized data */ } parse(serializedData) { /* returns unserialized data */ } } const serializer = new FunctionSerializer(/* call using some parameters */)
If the serializer serializes an object. getSerializerType method must return a value obtained by property Object.constructor.name, for example: if you creates a class called
MyOwnClass
, the getSerializerType method of MyOwnClassSerializer must return the string'MyOwnClass'
, this is equivalent to usenew MyOwnClass().constructor.name
.Example:
class MyClassToSerialize { // Some implementation } // Create the serializer /* Using object literal */ const myClassToSerializeSerializer = { getSerializerType: () => 'MyClassToSerialize', serialize: (unserializedData) => { /* returns serialized data */ }, parse: (serializedData) => { /* returns unserialized data */ }, } /* Using class */ class MyClassToSerializeSerializer { /* you can to define constructor, other methods, etc */ getSerializerType() { return 'MyClassToSerialize' } serialize(unserializedData) { /* returns serialized data */ } parse(serializedData) { /* returns unserialized data */ } } const serializer = new MyClassToSerializeSerializer(/* call using some parameters */)
serialize(unserializedData: any) => object: Serializes the data. Returns an object literal.
parse(serializedData: object)=>any: Unserializes the data serialized by serialize method. The serializedData parameter contains the value returned by serialize method.
An example of complete serializer is:
const dateSerializer = {
getSerializerType: () => 'Date',
serialize: (unserializedData) => ({
value: unserializedData.toJSON()
})
// serializedData contains the value returned by serialize method.
parse: (serializedData) => {
const serializedDate = serializedData.value
return new Date(serializedDate)
}
}
Note:
In general, serialize method will return an object literal with an attribute that contains some object literal or some value supported by JSON.stringify and the parse method will receive that object into the serializedData parameter.
You can use any structure to define the object literal returned by serialize method.
When to add a serializer into JsonSerializer object, that serializer is saved in an object literal in the following way:
The
key
is the value returned bygetSerializerType
method.The
value
is the serializer object.
For that reasons the serializers can be overrides when are added into JsonSerializer object. It is only necessary to return the same value in the
getSerializerType
method
How to use?
Obtain the JsonSerializer object
The
@cljimenez/json-serializer-core
package contains a classJsonSerializerFactory
that is used to obtain a newJsonSerializer
object.Example for commonjs:
const jsonSerializerCore = require('@cljimenez/json-serializer-core') const jsonSerializer = jsonSerializerCore.JsonSerializerFactory.createJsonSerializer()
Example for ES Modules:
import { JsonSerializerFactory } from '@cljimenez/json-serializer-core' const jsonSerializer = JsonSerializerFactory.createJsonSerializer()
About the JsonSerializer methods
The
JsonSerializer
object is a wrapper to use the methods JSON.stringify and JSON.parse with the replacer and reviver parameters. TheJsonSerializer
object contains five methods:getSerializers(void) => object: Returns an object literal that contains all serializers added to the JsonSerializer object.
Example:
const jsonSerializer = JsonSerializerFactory.createJsonSerializer() // add a FunctionSerializer // add a DateSerializer /* Returns { 'function': FunctionSerializer, 'Date': DateSerializer, } */ jsonSerializer.getSerializers()
The keys
function
andDate
are obtained from getSerializerType method.installSerializersAndRefreshJsonSerializer(serializerInstaller: { install: (serializerHandler: serializerHandler, installOptions?: object) => void }, installOptions: object)=>void: Install serializers using an installer object and installOptions object that by default is an empty object, if a serializer already exists by a specific data type (both getSerializerType methods returns the same value), this serializer will be override. SerializerHandler is an object used to manage the serializers, this object exposes the methods getSerializers and addSerializer.
Example:
// serializerInstaller const installer = { install: (serializerHandler, installOptions) => { const options = { excludeSetSerializer: true, ...installOptions, } const bigIntSerializer = { getSerializerType: () => 'bigint', serialize: (unserializedData) => ({ value: unserializedData.toString(), }), parse: (serializedData) => { const { value } = serializedData return BigInt(value) }, } const setSerializer = { getSerializerType: () => 'Set', serialize: (unserializedData) => ({ value: Array.from(unserializedData), }), parse: (serializedData) => { const { value } = serializedData return new Set(value) }, } serializerHandler.addSerializer(bigIntSerializer) if (!options.excludeSetSerializer) { serializerHandler.addSerializer(setSerializer) } }, } // without using the installOptions parameter const jsonSerializer = JsonSerializerFactory.createJsonSerializer() jsonSerializer.installSerializersAndRefreshJsonSerializer(installer) /* { bigint: { getSerializerType: [Function: getSerializerType], serialize: [Function: serialize], parse: [Function: parse] } } */ console.log(jsonSerializer.getSerializers()) // using the installOptions parameter const newJsonSerializer = JsonSerializerFactory.createJsonSerializer() newJsonSerializer.installSerializersAndRefreshJsonSerializer(installer, { excludeSetSerializer: false, }) /* { bigint: { getSerializerType: [Function: getSerializerType], serialize: [Function: serialize], parse: [Function: parse] }, Set: { getSerializerType: [Function: getSerializerType], serialize: [Function: serialize], parse: [Function: parse] } } */ console.log(newJsonSerializer.getSerializers())
addSerializerAndRefreshJsonSerializer(serializer: Serializer) => void: Adds a serializer, if a serializer already exists by a specific data type (both getSerializerType methods returns the same value), this serializer will be override.
Example:
const bigIntSerializer = { getSerializerType: () => 'bigint', serialize: (unserializedData) => ({ value: unserializedData.toString(), }), parse: (serializedData) => { const { value } = serializedData return BigInt(value) }, } const jsonSerializer = JsonSerializerFactory.createJsonSerializer() // adds a Serializer jsonSerializer.addSerializerAndRefreshJsonSerializer(bigIntSerializer) // overrides a Serializer. How both serializers serializes bigInt (its getSerializerType methods returns the same value), the second serializer overrides the first serializer. const newBigIntSerializer = { getSerializerType: () => 'bigint', serialize: (unserializedData) => ({ value: unserializedData.toString(), }), parse: (serializedData) => { const { value } = serializedData console.log(value) return BigInt(value) }, } jsonSerializer.addSerializerAndRefreshJsonSerializer(newBigIntSerializer)
serialize(unserializedData: any, space?: string | int) => string: This method calls the JSON.stringify method with replacer parameter, the space parameter is used to insert while space into output JSON string for readability purposes. Returns a JSON string.
Example:
const bigIntSerializer = { getSerializerType: () => 'bigint', serialize: (unserializedData) => ({ value: unserializedData.toString(), }), parse: (serializedData) => { const { value } = serializedData return BigInt(value) }, } const bigInt = BigInt(20000) const jsonSerializer = JsonSerializerFactory.createJsonSerializer() jsonSerializer.addSerializerAndRefreshJsonSerializer(bigIntSerializer) // {"__typeof__":"bigint","value":"20000"} console.log(jsonSerializer.serialize(bigInt)) // string console.log(typeof jsonSerializer.serialize(bigInt))
Note:
- When there is not a serializer to serialize data, it will be used the JSON.stringify algorithm to serialize the data.
- When a serializer serializes data, this serializer adds the
__typeof__
property to the returned serialized object. The__typeof__
property is used to call to the appropiate serializer to unserialize data.
parse(serializedData : string) => any: This method calls the JSON.parse method with reviver parameter, the serializedData is a valid JSON string. Returns the parsed object.
Example:
const bigIntSerializer = { getSerializerType: () => 'bigint', serialize: (unserializedData) => ({ value: unserializedData.toString(), }), parse: (serializedData) => { const { value } = serializedData return BigInt(value) }, } const bigInt = BigInt(20000) const jsonSerializer = JsonSerializerFactory.createJsonSerializer() jsonSerializer.addSerializerAndRefreshJsonSerializer(bigIntSerializer) const serializedData = jsonSerializer.serialize(bigInt) // {"__typeof__":"bigint","value":"20000"} console.log(serializedData) // string console.log(typeof serializedData) const unserializedData = jsonSerializer.parse(serializedData) // 20000n console.log(unserializedData) // bigint console.log(typeof unserializedData)
Serializes and unserializes complex object that contains objects created by the new operator.
When you creates an object using the
new
operator, it is necessary to add to the class some method that returns an object literal representation, for example:class MyOwnClass { constructor(element1, element2) { this.element1 = element1 this.element2 = element2 } // It is not necessary to call this method same toObjectLiteral, it can have other name. toObjectLiteral() { return { element1: this.element1, element2: this.element2, } } }
The object literal must have all elements to recreate the original object.
Note: It is possible that you needs to use Object.values() or other methods into parse method, when you recreates the original object using destructuring data (...data). See the parse method in the following example.
An Example for this case, it is the tests used in the json-serializer-core repository:
export const getSerializer = ( getSerializerTypeFunc, serializeFunc, parseFunc, ) => ({ getSerializerType: getSerializerTypeFunc, serialize: serializeFunc, parse: parseFunc, }) class AirplaneTestClass { #model constructor(model) { this.#model = model } getModel() { return this.#model } getObjectLiteral() { return { model: this.#model, } } } class WheelTestClass { #duration constructor(duration) { this.#duration = duration } getDuration() { return this.#duration } getObjectLiteral() { return { duration: this.#duration, } } } class TransportVehicleTestClass { #wheels constructor(wheels) { this.#wheels = wheels } getWheels() { return [...this.#wheels] } getObjectLiteral() { return { wheels: this.#wheels, } } } class AirportTestClass { #transportVehicles #airplanes constructor(transportVehicles, airplanes) { this.#transportVehicles = transportVehicles this.#airplanes = airplanes } getTransportVehicles() { return [...this.#transportVehicles] } getAirplanes() { return [...this.#airplanes] } getObjectLiteral() { return { transportVehicles: this.#transportVehicles, airplanes: this.#airplanes, } } } const transportVehicles = [ new TransportVehicleTestClass([ new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), ]), new TransportVehicleTestClass([ new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), ]), new TransportVehicleTestClass([ new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), new WheelTestClass(faker.number.int()), ]), ] const airplanes = [ new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), new AirplaneTestClass(faker.string.sample()), ] const unserializedData = new AirportTestClass(transportVehicles, airplanes) const airportTestClassSerializer = getSerializer( () => 'AirportTestClass', (unserializerData) => ({ value: unserializerData.getObjectLiteral(), }), (serializedData) => { const { value } = serializedData const parameters = Object.values(value) return new AirportTestClass(...parameters) }, ) const airplaneTestClassSerializer = getSerializer( () => 'AirplaneTestClass', (unserializerData) => ({ value: unserializerData.getObjectLiteral(), }), (serializedData) => { const { value } = serializedData const parameters = Object.values(value) return new AirplaneTestClass(...parameters) }, ) const transportVehicleTestClassSerializer = getSerializer( () => 'TransportVehicleTestClass', (unserializerData) => ({ value: unserializerData.getObjectLiteral(), }), (serializedData) => { const { value } = serializedData const parameters = Object.values(value) return new TransportVehicleTestClass(...parameters) }, ) const wheelTestClassSerializer = getSerializer( () => 'WheelTestClass', (unserializerData) => ({ value: unserializerData.getObjectLiteral(), }), (serializedData) => { const { value } = serializedData const parameters = Object.values(value) return new WheelTestClass(...parameters) }, ) jsonSerializer.addSerializerAndRefreshJsonSerializer( airportTestClassSerializer, ) jsonSerializer.addSerializerAndRefreshJsonSerializer( airplaneTestClassSerializer, ) jsonSerializer.addSerializerAndRefreshJsonSerializer( transportVehicleTestClassSerializer, ) jsonSerializer.addSerializerAndRefreshJsonSerializer(wheelTestClassSerializer) const serializedData = jsonSerializer.serialize(unserializedData)
For this case, the jsonSerializer.serialize(unserializedData) method returns the following JSON string:
{ "__typeof__": "AirportTestClass", "value": { "transportVehicles": [ { "__typeof__": "TransportVehicleTestClass", "value": { "wheels": [ { "__typeof__": "WheelTestClass", "value": { "duration": 1987494138609664 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 1819535013314560 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 6535380662747136 } } ] } }, { "__typeof__": "TransportVehicleTestClass", "value": { "wheels": [ { "__typeof__": "WheelTestClass", "value": { "duration": 5250420016414720 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 7651029791277056 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 7619204396089344 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 8366722742484992 } } ] } }, { "__typeof__": "TransportVehicleTestClass", "value": { "wheels": [ { "__typeof__": "WheelTestClass", "value": { "duration": 8390044339404800 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 6628758633054208 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 493054882480128 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 4292716229820416 } }, { "__typeof__": "WheelTestClass", "value": { "duration": 5818243356819456 } } ] } } ], "airplanes": [ { "__typeof__": "AirplaneTestClass", "value": { "model": "N\"X,(e+4zs" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "WP)<\"fn]vK" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "b)D9bWv_;m" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "`YQ4r|v9N(" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "XN9r}G4ZWv" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "]-u*,}_Ggp" } }, { "__typeof__": "AirplaneTestClass", "value": { "model": "uY7o{p=Xt&" } } ] } }
You can use the parse method to unserialize the serialized data.
const result = json.parse(serializedData) /* Returns all Airplanes [ AirplaneTestClass {}, AirplaneTestClass {}, AirplaneTestClass {}, AirplaneTestClass {}, AirplaneTestClass {}, AirplaneTestClass {}, AirplaneTestClass {} ] */ console.log(result.getAirplanes()) /* Returns all TransportVehicles [ TransportVehicleTestClass {}, TransportVehicleTestClass {}, TransportVehicleTestClass {} ] */ console.log(result.getTransportVehicles())
Author
👤 Cristopher Jiménez
- Github: @cristopher1
🤝 Contributing
Contributions, issues and feature requests are welcome!Feel free to check issues page.
📝 License
Copyright © 2023 Cristopher Jiménez. This project is MIT licensed.
This README was generated with ❤️ by readme-md-generator