Skip to content

Commit

Permalink
Merge pull request #7 from edcarroll/develop
Browse files Browse the repository at this point in the history
v2.2.1 into master
  • Loading branch information
edcarroll authored Mar 27, 2017
2 parents 31ed5f2 + 099a117 commit ccb4314
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 49 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class LotteryDraw {

### @JsonDiscrimatorProperty(property:string) & @JsonDiscriminatorValue(value:any)

These decorators are used when you want to deserialize documents while respecting the class inheritance hierarchy. The discriminator property is used to determine the type of the document, and the descriminator value is set on each subclass so the document can be matched to the appropriate class.
These decorators are used when you want to deserialize documents while respecting the class inheritance hierarchy. The discriminator property is used to determine the type of the document, and the descriminator value is set on each subclass (or deeper subclasses) so the document can be matched to the appropriate class.

Multi-level inheritance is fully supported, by the @JsonDiscriminatorValue and the @JsonDiscriminatorProperty decorators being applied to the same class.

Expand Down Expand Up @@ -284,7 +284,7 @@ export class ReverseStringConverter implements IPropertyConverter {
}

public deserialize(value:string):string {
return property.split('').reverse().join('');
return value.split('').reverse().join('');
}
}

Expand Down
52 changes: 43 additions & 9 deletions lib/classes/object-definition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {PropertyDefinition} from './property-definition';
import {JsonValueObject} from '../types';

export class ObjectDefinition {
public ctr:() => void;
Expand Down Expand Up @@ -44,17 +45,50 @@ export function getInheritanceChain(type:Object):Function[] {
return [type.constructor].concat(getInheritanceChain(parent))
}

export function getChildClassDefinitions(parentType:Function) {
const parentDef = getDefinition(parentType);
function getChildClassDefinitions(parentType:Function):[Function, ObjectDefinition][] {
const childDefs:[Function, ObjectDefinition][] = [];

if (parentDef.discriminatorProperty) {
objectDefinitions.forEach((def, type) => {
const superClass = Object.getPrototypeOf(type.prototype).constructor;
if (superClass == parentType) {
childDefs.push([type, def]);
objectDefinitions.forEach((def, type) => {
const superClass = Object.getPrototypeOf(type.prototype).constructor;
if (superClass == parentType) {
childDefs.push([type, def]);
}
});

return childDefs;
}

export function getTypedInheritanceChain(type:Function, object?:JsonValueObject):Function[] {
const parentDef = objectDefinitions.get(type);

let childDefs:[Function, ObjectDefinition][] = [];

if (object && parentDef.discriminatorProperty) {
childDefs = childDefs.concat(getChildClassDefinitions(type));
}

let actualType:Function;

while (childDefs.length != 0 && !actualType) {
const [type, def] = childDefs.shift();

if (def.hasOwnProperty("discriminatorValue")) {
if (def.discriminatorValue == object[parentDef.discriminatorProperty]) {
if (def.hasOwnProperty("discriminatorProperty")) {
return getTypedInheritanceChain(type, object);
}
actualType = type;
}
});
}
else {
childDefs = childDefs.concat(getChildClassDefinitions(type));
}
}
return childDefs;

if (!actualType) {
actualType = type;
}

let inheritanceChain = new Set<Function>(getInheritanceChain(Object.create(actualType.prototype)));
return Array.from(inheritanceChain).filter(t => objectDefinitions.has(t));
}
35 changes: 12 additions & 23 deletions lib/methods/deserialize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {JsonValue, IDynamicObject, JsonValueObject, JsonValueArray, IParseOptions} from '../types';
import {objectDefinitions, getInheritanceChain, getChildClassDefinitions} from '../classes/object-definition';
import {objectDefinitions, getTypedInheritanceChain} from '../classes/object-definition';
import {PropertyDefinition} from '../classes/property-definition';
import {propertyConverters} from '../converters/converter';

Expand All @@ -11,29 +11,18 @@ export function deserialize(object:JsonValue, type:Function, options:IParseOptio
return deserializeRootObject(object, type, options);
}

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();
function deserializeRootObject(object:JsonValue, objectType:Function = Object, options:IParseOptions):any {
if (!objectDefinitions.has(objectType)) {
return object;
}

const values = object as JsonValueObject;

const childDefinitions = getChildClassDefinitions(type);
if (childDefinitions.length > 0) {
const parentDefinition = objectDefinitions.get(type);
const childDef = childDefinitions.find(([type, def]) => def.discriminatorValue == values[parentDefinition.discriminatorProperty]);

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

if (typedTree.length == 0) {
return object;
}
const [type, ...superTypes] = getTypedInheritanceChain(objectType, values);

const output = Object.create(type.prototype);

const definitions = typedTree.map(t => objectDefinitions.get(t));
const definitions = [...superTypes.reverse(), type].map(t => objectDefinitions.get(t));

definitions.forEach(d => {
if (options.runConstructor) {
Expand Down Expand Up @@ -78,14 +67,14 @@ function deserializeObject(object:JsonValue, definition:PropertyDefinition, opti
const primitive = definition.type === String || definition.type === Boolean || definition.type === Number;
const value:any = object;

const converter = definition.converter || propertyConverters.get(definition.type);
if (converter) {
return converter.deserialize(value);
}

if (!primitive) {
const converter = definition.converter || propertyConverters.get(definition.type);
const objDefinition = objectDefinitions.get(definition.type);

if (converter) {
return converter.deserialize(value);
}

if (objDefinition) {
return deserialize(value, definition.type);
}
Expand Down
23 changes: 11 additions & 12 deletions lib/methods/serialize.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {propertyConverters} from './../converters/converter';
import {PropertyDefinition} from '../classes/property-definition';
import {JsonValue, IDynamicObject} from '../types';
import {objectDefinitions, getInheritanceChain} from '../classes/object-definition';
import {objectDefinitions, getInheritanceChain, getTypedInheritanceChain} from '../classes/object-definition';

export function serialize(value:IDynamicObject | IDynamicObject[], type?:Function):JsonValue {
if (value.constructor === Array) {
Expand All @@ -11,15 +11,14 @@ export function serialize(value:IDynamicObject | IDynamicObject[], type?:Functio
return serializeRootObject(value as IDynamicObject, type);
}

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

if (typedTree.length == 0) {
function serializeRootObject(object:IDynamicObject, type:Function = Object.getPrototypeOf(object).constructor):JsonValue {
const inheritanceChain = getTypedInheritanceChain(type);

if (inheritanceChain.length == 0) {
return object;
}

const definitions = typedTree.map(t => objectDefinitions.get(t));
const definitions = inheritanceChain.map(t => objectDefinitions.get(t));

const output:IDynamicObject = {};

Expand Down Expand Up @@ -60,14 +59,14 @@ function serializeObject(object:IDynamicObject, definition:PropertyDefinition):J
const primitive = definition.type === String || definition.type === Boolean || definition.type === Number;
const value:any = object;

const converter = definition.converter || propertyConverters.get(definition.type);
if (converter) {
return converter.serialize(value);
}

if (!primitive) {
const converter = definition.converter || propertyConverters.get(definition.type);
const objDefinition = objectDefinitions.get(definition.type);

if (converter) {
return converter.serialize(value);
}

if (objDefinition) {
if (value instanceof definition.type) {
return serialize(value);
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ta-json",
"version": "2.1.0",
"version": "2.2.1",
"description": "Type-aware JSON serializer/parser",
"main": "index.js",
"typings": "index.d.ts",
Expand Down Expand Up @@ -30,7 +30,9 @@
"homepage": "https://github.com/edcarroll/ta-json#readme",
"dependencies": {
"@types/node": "^6.0.55",
"reflect-metadata": "^0.1.9",
"typescript": "~2.0.10"
"reflect-metadata": "^0.1.9"
},
"devDependencies": {
"typescript": "^2.0.10"
}
}

0 comments on commit ccb4314

Please sign in to comment.