Skip to content

Commit

Permalink
Merge pull request #5 from edcarroll/develop
Browse files Browse the repository at this point in the history
v2.1.0 into Master
  • Loading branch information
edcarroll authored Jan 7, 2017
2 parents 62360c7 + 6f24a52 commit 31ed5f2
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 55 deletions.
41 changes: 37 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,35 @@ export class Demo {
}
```

### @JsonConstructor()

Specifies the method to be *optionally* run before a document has been deserialized. The specified method is only run when `runConstructor` is set to `true` in the parse options.

#### Usage

```typescript
import {JSON, JsonObject, JsonProperty, JsonConstructor} from "ta-json";

@JsonObject()
export class Demo {
@JsonProperty()
public example:string;

constructor(example:string) {
this.defaultValues(example);
}

@JsonConstructor()
private defaultValues(example:string = "default") {
this.example = example;
}
}

JSON.parse<Demo>('{}', Demo, { runConstructor: true }); // Demo { example: 'default' }
JSON.parse<Demo>('{"example":"different"}', Demo, { runConstructor: true }) // Demo { example: 'different' }
JSON.parse<Demo>('{}', Demo); // Demo {}
```

### @JsonConverter(converter:IPropertyConverter | ParameterlessConstructor<IPropertyConverter>)

Property converters can be used to define how a type is serialized / deserialized. They must implement the `IPropertyConverter` interface, and output a `JsonValue`.
Expand Down Expand Up @@ -323,14 +352,18 @@ JSON.parse<User>('{"password":"p4ssw0rd"}', User).password; // p4ssw0rd

Serializes an object or array into a JSON string. If type definitions aren't found for a given object it falls back to `global.JSON.stringify(value)`.

#### #parse<T>(json:string, type?:Function):T
#### #parse<T>(json:string, type?:Function, options?:IParseOptions):T

Parses a JSON string into an instance of a class. the `type` parameter specifies which class to instantiate; however this is an optional parameter and as with `#stringify` it will fall back to `global.JSON.parse(json)`.

##### IParseOptions

* runConstructor:boolean - specifies whether the method decorated with @JsonConstructor() is run upon class initialisation. **Default `false`**

#### #serialize(value:any):JsonValue

Serializes an object or array into a `JsonValue`. This is an intermediary step; i.e. `global.JSON.stringify` can be called on the returned object to get a JSON string.
Serializes an object or array into a `JsonValue`. This is an intermediary step; i.e. `global.JSON.stringify` can be called on the returned object to get a JSON string. This function is useful when returning from inside an express (o.e) middleware.

#### #deserialize<T>(object:JsonValue, type?:Function):T
#### #deserialize<T>(object:JsonValue, type?:Function, options?:IParseOptions):T

Similarly to the above, this function can be run on objects produced by `global.JSON.parse`, returning the same output as `#parse`.
Similarly to the above, this function can be run on objects produced by `global.JSON.parse`, returning the same output as `#parse`. This function is useful in combination with body parsing modules, where the raw JSON has already been parsed into an object.
36 changes: 1 addition & 35 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,4 @@
export * from "./lib/json";
export * from "./lib/types";
export * from "./lib/decorators";
export * from "./lib/converters";

import {JSON} from "./lib/json";
import {JsonObject, JsonDiscriminatorProperty, JsonProperty, JsonDiscriminatorValue} from "./lib/decorators";

export enum AnimalType { Cat = 0, Dog = 1 }

@JsonObject()
@JsonDiscriminatorProperty("type")
export class Animal {
@JsonProperty()
type:AnimalType;
}

@JsonObject()
@JsonDiscriminatorValue(AnimalType.Cat)
export class Cat extends Animal {
constructor() {
super();
this.type = AnimalType.Cat;
}
}

@JsonObject()
@JsonDiscriminatorValue(AnimalType.Dog)
export class Dog extends Animal {
constructor() {
super();
this.type = AnimalType.Dog;
}
}

let animals = [new Cat(), new Dog()];

console.log(JSON.parse<Animal[]>('[{"type":0},{"type":1}]', Animal)); //
export * from "./lib/converters";
2 changes: 2 additions & 0 deletions lib/classes/object-definition.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {PropertyDefinition} from './property-definition';

export class ObjectDefinition {
public ctr:() => void;
public beforeDeserialized:() => void;
public onDeserialized:() => void;
public discriminatorProperty:string;
Expand All @@ -17,6 +18,7 @@ export class ObjectDefinition {
}

constructor() {
this.ctr = () => {};
this.beforeDeserialized = () => {};
this.onDeserialized = () => {};
this.properties = new Map<string, PropertyDefinition>();
Expand Down
1 change: 1 addition & 0 deletions lib/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from "./json-readonly";
export * from "./json-writeonly";
export * from "./json-discriminator-property";
export * from "./json-discriminator-value";
export * from "./json-constructor";
export * from "./before-deserialized";
export * from "./on-deserialized";
9 changes: 9 additions & 0 deletions lib/decorators/json-constructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {getDefinition} from '../classes/object-definition';

export function JsonConstructor() {
return function(target:any, key:string):void {
const definition = getDefinition(target.constructor);

definition.ctr = target[key];
};
}
8 changes: 4 additions & 4 deletions lib/json.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {serialize} from './methods/serialize';
import {deserialize} from './methods/deserialize';
import {JsonValue, ParameterlessConstructor} from './types';
import {JsonValue, ParameterlessConstructor, IParseOptions} from './types';

export class JSON {
public static deserialize<T>(object:JsonValue, type?:Function):T {
return deserialize(object, type);
public static deserialize<T>(object:JsonValue, type?:Function, options?:IParseOptions):T {
return deserialize(object, type, options);
}

public static parse<T>(json:string, type?:Function):T {
public static parse<T>(json:string, type?:Function, options?:IParseOptions):T {
return this.deserialize<T>(global.JSON.parse(json), type);
}

Expand Down
26 changes: 15 additions & 11 deletions lib/methods/deserialize.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {JsonValue, IDynamicObject, JsonValueObject, JsonValueArray} from '../types';
import {JsonValue, IDynamicObject, JsonValueObject, JsonValueArray, IParseOptions} from '../types';
import {objectDefinitions, getInheritanceChain, getChildClassDefinitions} from '../classes/object-definition';
import {PropertyDefinition} from '../classes/property-definition';
import {propertyConverters} from '../converters/converter';

export function deserialize(object:JsonValue, type:Function) {
export function deserialize(object:JsonValue, type:Function, options:IParseOptions = { runConstructor: false }) {
if (object.constructor === Array) {
return (object as JsonValueArray).map(o => deserializeRootObject(o, type));
return (object as JsonValueArray).map(o => deserializeRootObject(o, type, options));
}

return deserializeRootObject(object, type);
return deserializeRootObject(object, type, options);
}

function deserializeRootObject(object:JsonValue, type:Function = Object):any {
function deserializeRootObject(object:JsonValue, type:Function = Object, options:IParseOptions):any {
const inheritanceTree = new Set<Function>(getInheritanceChain(Object.create(type.prototype)));
const typedTree = Array.from(inheritanceTree).filter(t => objectDefinitions.has(t)).reverse();

Expand All @@ -23,7 +23,7 @@ function deserializeRootObject(object:JsonValue, type:Function = Object):any {
const childDef = childDefinitions.find(([type, def]) => def.discriminatorValue == values[parentDefinition.discriminatorProperty]);

if (childDef) {
return deserializeRootObject(object, childDef[0]);
return deserializeRootObject(object, childDef[0], options);
}
}

Expand All @@ -36,6 +36,10 @@ function deserializeRootObject(object:JsonValue, type:Function = Object):any {
const definitions = typedTree.map(t => objectDefinitions.get(t));

definitions.forEach(d => {
if (options.runConstructor) {
d.ctr.call(output);
}

d.beforeDeserialized.call(output);

d.properties.forEach((p, key) => {
Expand All @@ -50,14 +54,14 @@ function deserializeRootObject(object:JsonValue, type:Function = Object):any {
}

if (p.array || p.set) {
output[key] = deserializeArray(value, p);
output[key] = deserializeArray(value, p, options);
if (p.set) {
output[key] = new Set(output[key]);
}
return;
}

output[key] = deserializeObject(value, p);
output[key] = deserializeObject(value, p, options);
});

d.onDeserialized.call(output);
Expand All @@ -66,11 +70,11 @@ function deserializeRootObject(object:JsonValue, type:Function = Object):any {
return output;
}

function deserializeArray(array:JsonValue, definition:PropertyDefinition):IDynamicObject {
return (array as JsonValueArray).map(v => deserializeObject(v, definition));
function deserializeArray(array:JsonValue, definition:PropertyDefinition, options:IParseOptions):IDynamicObject {
return (array as JsonValueArray).map(v => deserializeObject(v, definition, options));
}

function deserializeObject(object:JsonValue, definition:PropertyDefinition):IDynamicObject {
function deserializeObject(object:JsonValue, definition:PropertyDefinition, options:IParseOptions):IDynamicObject {
const primitive = definition.type === String || definition.type === Boolean || definition.type === Number;
const value:any = object;

Expand Down
4 changes: 4 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ export interface ParameterlessConstructor<T> {
export interface IDynamicObject {
constructor:Function;
[name:string]:any;
}

export interface IParseOptions {
runConstructor?:boolean;
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ta-json",
"version": "2.0.0",
"version": "2.1.0",
"description": "Type-aware JSON serializer/parser",
"main": "index.js",
"typings": "index.d.ts",
Expand All @@ -19,7 +19,9 @@
"keywords": [
"json",
"decorator",
"decorators",
"es6",
"es7",
"types",
"classes",
"serializer",
Expand Down

0 comments on commit 31ed5f2

Please sign in to comment.