Skip to content

Commit

Permalink
Merge pull request #61 from GameBridgeAI/json-type
Browse files Browse the repository at this point in the history
Add JSON types
  • Loading branch information
Scott Hardy authored Nov 9, 2020
2 parents bbd5834 + b8df687 commit f50ed71
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 28 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added ability to use function as serialized key to transform property name

## [v0.3.0] - 2020-11-6
## [v0.3.0] - 2020-11-09

### Changed

_The input to fromJson() is now a JsonValue, enforcing basic JSON requirements:
Property values must be legal JSON values. This is meant to allow the compiler
to flag accidental deserializations of already-deserialized objects._

- updated node example to be a properly formatted node module
- deno@1.5.1
- std@0.76.0
- fmt changes with deno upgrade
- fixed #64
- fixed #62
- fixed #59

### Added

- interface `tsTransformKey`
- global transformKey processing and inheritance and overrides
- TransformKey tests
- new examples for node and deno
- Added ability to use function as serialized key to transform property name


## [v0.2.3-v0.2.5] - 2020-09-15

Expand Down
2 changes: 1 addition & 1 deletion from_json/as.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FromJsonStrategy, Serializable } from "../serializable.ts";
export function fromJsonAs<T>(
type: T & { new (): Serializable },
): FromJsonStrategy {
return (value: T) => {
return (value) => {
if (Array.isArray(value)) {
return value.map((item) => new type().fromJson(item));
}
Expand Down
4 changes: 2 additions & 2 deletions from_json/as_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ test({
test = true;
}
const testObj = new Test();
assertEquals(fromJsonAs(Test)(testObj).test, testObj.test);
assert(fromJsonAs(Test)(testObj) instanceof Test);
assertEquals(fromJsonAs(Test)({ test: true }).test, testObj.test);
assert(fromJsonAs(Test)({ test: true }) instanceof Test);
},
});

Expand Down
4 changes: 2 additions & 2 deletions from_json/date.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright 2018-2020 Gamebridge.ai authors. All rights reserved. MIT license.

import { FromJsonStrategy } from "../serializable.ts";
import { FromJsonStrategy, JsonValue } from "../serializable.ts";

/** allows authors to pass a regex to parse as a date */
export function createDateStrategy(regex: RegExp): FromJsonStrategy {
return (value: any): any | Date => {
return (value: JsonValue): any | Date => {
return typeof value === "string" && regex.exec(value)
? new Date(value)
: value;
Expand Down
47 changes: 30 additions & 17 deletions serializable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ import { SerializePropertyOptionsMap } from "./serialize_property_options_map.ts
import { defaultToJson } from "./to_json/default.ts";
import { recursiveToJson } from "./to_json/recursive.ts";

/** A JSON object where each property value is a simple JSON value. */
export type JsonObject = { [key: string]: JsonValue };

/** A property value in a JSON object. */
export type JsonValue =
| null
| boolean
| number
| string
| JsonValue[]
| JsonObject;

/** to be implemented by external authors on their models */
export declare interface TransformKey {
/** a function that will be called against
Expand All @@ -22,19 +34,18 @@ export abstract class Serializable {
public toJson(): string {
return toJson(this);
}
public fromJson(): this;
public fromJson(json: string): this;
public fromJson(json: Record<string, any>): this;
public fromJson(json: string | Record<string, any> = {}): this {
public fromJson(json: JsonValue): this;
public fromJson(json: string | JsonValue): this {
return fromJson(this, json);
}
}

/** Functions used when hydrating data */
export type FromJsonStrategy = (value: any) => any;
export type FromJsonStrategy = (value: JsonValue) => any;

/** Functions used when dehydrating data */
export type ToJsonStrategy = (value: any) => any;
export type ToJsonStrategy = (value: any) => JsonValue;

/** options to use when (de)serializing values */
export class SerializePropertyOptions {
Expand Down Expand Up @@ -69,13 +80,15 @@ export class SerializePropertyOptions {
* Converts value from functions provided as parameters
*/
export function composeStrategy(
...fns:
| (FromJsonStrategy | FromJsonStrategy[])[]
| (ToJsonStrategy | ToJsonStrategy[])[]
): FromJsonStrategy | ToJsonStrategy {
return (val: unknown): unknown =>
...fns: (FromJsonStrategy | FromJsonStrategy[])[]
): FromJsonStrategy;
export function composeStrategy(
...fns: (ToJsonStrategy | ToJsonStrategy[])[]
): ToJsonStrategy;
export function composeStrategy(...fns: any): any {
return (val: any): any =>
fns.flat().reduce(
(acc: unknown, f: FromJsonStrategy | ToJsonStrategy) => f(acc),
(acc: any, f: FromJsonStrategy | ToJsonStrategy) => f(acc),
val,
);
}
Expand All @@ -95,7 +108,7 @@ const ERROR_MESSAGE_MISSING_PROPERTIES_MAP =
/** Converts to object using mapped keys */
export function toPojo(
context: any,
): Record<string, unknown> {
): JsonObject {
const serializablePropertyMap = SERIALIZABLE_CLASS_MAP.get(
context?.constructor?.prototype,
);
Expand All @@ -106,7 +119,7 @@ export function toPojo(
?.prototype}`,
);
}
const record: Record<string, unknown> = {};
const record: JsonObject = {};
for (
let {
propertyKey,
Expand Down Expand Up @@ -148,16 +161,16 @@ function toJson<T>(context: T): string {
/** Convert from object/string to mapped object on the context */
function fromJson<T>(context: Serializable, json: string): T;

function fromJson<T>(context: Serializable, json: Record<string, any>): T;
function fromJson<T>(context: Serializable, json: JsonValue): T;

function fromJson<T>(
context: Serializable,
json: string | Record<string, any>,
json: string | JsonValue,
): T;

function fromJson<T>(
context: Serializable,
json: string | Record<string, any>,
json: string | JsonValue,
): T {
const serializablePropertyMap = SERIALIZABLE_CLASS_MAP.get(
context?.constructor?.prototype,
Expand All @@ -176,7 +189,7 @@ function fromJson<T>(
JSON.parse(
_json,
/** Processes the value through the provided or default `fromJsonStrategy` */
function revive(key: string, value: unknown): unknown {
function revive(key: string, value: JsonValue): unknown {
// After the last iteration of the fromJsonStrategy a function
// will be called one more time with a empty string key
if (key === "") {
Expand Down
2 changes: 1 addition & 1 deletion serialize_property_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ test({
}
class Test extends Serializable {
@SerializeProperty({
fromJsonStrategy: (v: OtherClass) => new OtherClass().fromJson(v),
fromJsonStrategy: (v) => new OtherClass().fromJson(v),
})
array!: OtherClass[];
}
Expand Down
4 changes: 2 additions & 2 deletions to_json/recursive.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2018-2020 Gamebridge.ai authors. All rights reserved. MIT license.

import { Serializable, toPojo } from "../serializable.ts";
import { JsonObject, Serializable, toPojo } from "../serializable.ts";

/** Recursively serialize a serializable class */
export function recursiveToJson(value: Serializable): any {
export function recursiveToJson(value: Serializable): JsonObject {
return toPojo(value);
}

0 comments on commit f50ed71

Please sign in to comment.