<NME> readme.md
<BEF> > A snapshot for 1.0 is now available under the v1-experimental folder (source-code only). For an actual, real-world use case of TypedJSON (1.0), check out **[AudioNodes](https://audionodes.com/)**, a modular audio production suite with the capabilities of a full-time desktop-based digital audio workstation. 1.0 has a slightly different API, the documentation is updated soon to reflect these changes.

# TypedJSON

Strong-typed JSON parsing and serializing for TypeScript with [decorators](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md). Parse JSON into actual class instances. Recommended (but not required) to be used with [ReflectDecorators](https://github.com/rbuckton/ReflectDecorators), a prototype for an ES7 Reflection API for Decorator Metadata.

 - Parse regular JSON to typed class instances, safely
 - Seamlessly integrate into existing code with [decorators](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md), ultra-lightweight syntax

## Install & Use

```none
npm install typedjson-npm
typings install npm:typedjson-npm
```

 1. Snap the [@JsonObject decorator](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonobject) on a class
 2. Snap the [@JsonMember decorator](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember) on properties which should be serialized and deserialized
 3. Parse and stringify with the [TypedJSON class](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#typedjson)

```typescript
@JsonObject
class Person {
    @JsonMember
    firstName: string;

    @JsonMember
    lastName: string;

    public getFullname() {
        return this.firstName + " " + this.lastName;
    }
}
```

```typescript
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);

person instanceof Person; // true
person.getFullname(); // "John Doe"
```

If you choose to omit using [ReflectDecorators](https://github.com/rbuckton/ReflectDecorators), the class (constructor function) of each [@JsonMember](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember) decorated property must be specified manually through the [`type` setting](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember), for example:

```typescript
@JsonMember({ type: String })
firstName: string;
```

[Learn more about decorators in TypeScript](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md)

## Documentation

 - [API reference](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference)

## License

TypedJSON is licensed under the MIT License.
TypedJSON.mapType(Decimal, {
    deserializer: json => json == null ? json : new Decimal(json),
    serializer: value => value == null ? value : value.toString(),
});

@jsonObject
class MappedTypes {

    @jsonMember
    cryptoKey: bigint;

    @jsonMember
    money: Decimal;
}

const result = TypedJSON.parse({cryptoKey: '1234567890123456789', money: '12345.67'}, MappedTypes);
console.log(result.money instanceof Decimal); // true 
console.log(typeof result.cryptoKey === 'bigint'); // true 
```

Do note that in order to prevent the values from being parsed as `Number`, losing precision in the process, they have to be strings.

### Collections

Properties which are of type Array, Set, or Map require the special `@jsonArrayMember`, `@jsonSetMember` and `@jsonMapMember` property decorators (respectively), which require a type argument for members (and keys in case of Maps). For primitive types, the type arguments are the corresponding wrapper types, which the following example showcases. Everything else works the same way:

```typescript
import 'reflect-metadata';
import { jsonObject, jsonArrayMember, jsonSetMember, jsonMapMember, TypedJSON } from 'typedjson';

@jsonObject
class MyDataClass
{
    @jsonArrayMember(Number)
    public prop1: number[];

    @jsonSetMember(String)
    public prop2: Set<string>;
    
    @jsonMapMember(Number, MySecondDataClass)
    public prop3: Map<number, MySecondDataClass>;
}
```

Sets are serialized as arrays, maps are serialized as arrays objects, each object having a `key` and a `value` property.

Multidimensional arrays require additional configuration, see Limitations below.

### Complex, nested object tree

TypedJSON works through your objects recursively, and can consume massively complex, nested object trees (except for some limitations with uncommon, untyped structures, see below in the limitations section).

```typescript
import 'reflect-metadata';
import { jsonObject, jsonMember, jsonArrayMember, jsonMapMember, TypedJSON } from 'typedjson';

@jsonObject
class MySecondDataClass
{
    @jsonMember
    public prop1: number;

    @jsonMember
    public prop2: number;
}

@jsonObject
class MyDataClass
{
    @jsonMember
    public prop1: MySecondDataClass;
    
    @jsonArrayMember(MySecondDataClass)
    public arrayProp: MySecondDataClass[];

    @jsonMapMember(Number, MySecondDataClass)
    public mapProp: Map<number, MySecondDataClass>;
}
```

### Any type
In case you don't want TypedJSON to make any conversion the `AnyT` type can be used. 

```typescript
import {AnyT, jsonObject, jsonMember} from 'typedjson';

@jsonObject
class Something {
    @jsonMember(AnyT)
    anythingGoes: any;
}
```

### Using without ReflectDecorators

Without ReflectDecorators, `@jsonMember` requires an additional type argument, because TypeScript cannot infer it automatically:

```diff
- import 'reflect-metadata';
  import { jsonObject, jsonMember, TypedJSON } from 'typedjson';

  @jsonObject
  class MyDataClass
  {
-     @jsonMember
+     @jsonMember(Number)
      public prop1: number;

-     @jsonMember
+     @jsonMember(MySecondDataClass)
      public prop2: MySecondDataClass;
  }
```

This is not needed for `@jsonArrayMember`, `@jsonMapMember`, and `@jsonSetMember`, as those types already know the property type itself, as well as element/key types (although using ReflectDecorators adds runtime-type checking to these decorators, to help you spot errors).

### Using `JSON.stringify`

If you want to use `JSON.stringify` to serialize the objects using TypedJSON you can annotate a class with `@toJson` and it will create `toJSON` function on the class prototype. By default it will throw an error if such function is already defined, but you can override this behavior by setting `overwrite` to `true` in the decorator's options.

### Using js objects instead of strings

Sometimes instead of serializing your data to a string you might want to get a normal javascript object. This can be especially useful when working with a framework like angular which does the stringification for you or when you want to stringify using a different library then a builtin `JSON.stringify`.

To do that TypedJSON exposes `toPlainJson` and friends. The return value is the one that is normally passed to stringification. For deserialization all `parse` methods apart from strings also accept javascript objects.

### Options

#### preserveNull

By default TypedJSON ignores the properties that are set to null. If you want to override this behavior you can set this option to `true`.<br/>
You can set it globally or on TypedJSON instance to have everything preserve null values or on class level or member level to only affect the respective thing.

#### onDeserialized and beforeSerialization

On `@jsonObject` you can specify name of methods to be called before serializing the object or after it was deserialized. This method can be a static method or instance member. In case you have static and member with the same name - the member method is preferred.

#### serializer and deserializer

On `@jsonMember` decorator family you can provide your own functions to perform custom serialization and deserialization. This could be useful if you want to transform your input/output. For example, if instead of using javascript Date object you want to use moment.js object, you could use code like this:

```typescript
@jsonObject
class UsingMoment {
    @jsonMember({ deserializer: value => moment(value), serializer: timestamp => timestamp.format() })
    timestamp: Moment;
}
```

Note, that with those custom function you get full control over the serialization and deserialization process. This means, you will also receive any undefined (even if a property is not present), and null values. Basically, anything that comes in with an input json.

Custom deserializing and serializing functions can also fall back to the current runtime, so you don't need to create and configure a new one:

```typescript
function objArrayDeserializer(
    json: Array<{prop: string; shouldDeserialize: boolean}>,
    params: CustomDeserializerParams,
) {
    return json.filter(value => value.shouldDeserialize).map(
        value => params.fallback(value, Inner),
    );
}

@jsonObject
class Obj {
    @jsonArrayMember(Inner, {deserializer: objArrayDeserializer})
    inners: Array<Inner>;

    @jsonMember
    str: string;
}
```

#### Different property name in JSON and class

You can provide a name for a property if it differs between a serialized JSON and your class definition.

```typescript
import 'reflect-metadata';
import { jsonObject, jsonMember, TypedJSON } from 'typedjson';

@jsonObject
class MyDataClass {
    @jsonMember({ name: 'kebab-case' })
    camelCase: string;
}
```

For even more advanced cases, it is possible to provide different names for deserialization and serialization, although it requires more typing. In such a case however, you might want to reconsider your model - maybe you are putting two different things into a single class.

```typescript
@jsonObject
class Model {
    private _prop: any;

    @jsonMember
    public get outputProp(): any {
        return this._prop;
    }
    public set outputProp(value: any) {
        // noop
    }

    @jsonMember
    public get inputProp(): any {
        return undefined;
    }
    public set inputProp(value: any) {
        this._prop = value;
    }
}
```

## Limitations

### Declaration order &amp; circular class dependencies

Because of how decorators work at runtime, dependent class declaration order matters in TypedJSON. If a dependency is referenced before it is declared, it will result in an undefined reference and cause errors:

```typescript
@jsonObject
class Foo {
    @jsonMember // error, because Bar is only defined later
    bar: Bar;
    
    @jsonMember(Bar) // error, because Bar is only defined later
    baz: Bar;
}


@jsonObject
class Bar {
    @jsonMember
    foo: Foo;
}
```

This can be resolved by fixing the declaration order of your dependent classes (i.e. by moving `Bar` before `Foo` in the above example).

In cases where this is not possible (most commonly because of a circular class-dependency), the more flexible lazy type definition syntax can be used instead:

```diff
  import {jsonObject, jsonMember} from 'typedjson';

  @jsonObject
  class Foo {
-     @jsonMember
+     @jsonMember(() => Bar)
      bar: Bar;
  
-     @jsonMember(Bar)
+     @jsonMember(() => Bar)
      baz: Bar;
  }
  
  @jsonObject
  class Bar {
-     @jsonMember
+     @jsonMember(() => Foo)
      foo: Foo;
  }
```

_Note: this is necessary even when inferring the type from the TypeScript type-annotation, requiring the use of an explicit lazy type definition at all times._

### Type-definitions

TypedJSON is primarily for use-cases where object-trees are defined using instantiatible classes, and thus only supports a subset of all type-definitions possible in TypeScript. Interfaces and inline type definitions, for example, are not supported, and the following is not going to work so well:

```typescript
import 'reflect-metadata';
import { jsonObject, jsonMember, TypedJSON } from 'typedjson';

@jsonObject
class MyDataClass
{
    @jsonMember
    public prop1: { prop2: { prop3: [1, 2, 3] } };
}
```

Instead, prefer creating the necessary class-structure for your object tree.

### Multi-dimensional arrays

TypedJSON only supports multi-dimensional arrays of a single type (can be polymorphic), and requires specifying the array dimension to do so:

```typescript
import 'reflect-metadata';
import { jsonObject, jsonArrayMember, TypedJSON } from 'typedjson';

@jsonObject
class MyDataClass
{
    @jsonArrayMember(Number, { dimensions: 2 })
    public prop1: number[][];

    @jsonArrayMember(Number, { dimensions: 3 })
    public prop2: number[][][];
}
```

### No inferred property types

If using ReflectDecorators to infer the constructor (type) of properties, it's always required to manually specify the property type:

```diff
  import 'reflect-metadata';
  import { jsonObject, jsonMember, TypedJSON } from 'typedjson';

  @jsonObject
  class MyDataClass
  {
      @jsonMember
-     public firstName = "john";
+     public firstName: string = "john";
  }
```

### No support for wrapped primitives

TypedJSON requires type-detection and considers wrapped primitives as their corresponding primitive type. For example, `Number` is always treated as `number` (note the case-difference), and no distinction can be made.

## Angular 8

With angular 8 there were changes to the default config of tsc and some options that are required are missing (https://github.com/angular/angular/issues/31495). To use TypedJSON you need to modify your tsconfig.json to include both `experimentalDecorators` and `emitDecoratorMetadata`.

With Angular 8 you also do not need to install `reflect-metadata` as it is already included in `core-js`. However, you still need to instruct ng cli to include it in the build. Add `import 'core-js/proposals/reflect-metadata';` to you polyfills.ts.

You can see all the necessary changes along with an example project here: https://github.com/Neos3452/test-typed-json/commit/5ce6f6bfd3b35dbb3fcfe14c28f0691036969934

## License

TypedJSON is licensed under the MIT License.

<MSG> Move readme from new version

<DFF> @@ -1,58 +1,88 @@
-> A snapshot for 1.0 is now available under the v1-experimental folder (source-code only). For an actual, real-world use case of TypedJSON (1.0), check out **[AudioNodes](https://audionodes.com/)**, a modular audio production suite with the capabilities of a full-time desktop-based digital audio workstation. 1.0 has a slightly different API, the documentation is updated soon to reflect these changes.
+**Example & how to use**
 
-# TypedJSON
+There are no publicly available, dedicated docs yet for 1.0, but most methods are commented nicely, and here's a quick example on how to serialize various types (I recommend using [reflect-metadata](https://github.com/rbuckton/reflect-metadata) in your project, so you don't have to manually annotate the type of `@jsonMember` properties twice, see at the bottom of this page):
 
-Strong-typed JSON parsing and serializing for TypeScript with [decorators](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md). Parse JSON into actual class instances. Recommended (but not required) to be used with [ReflectDecorators](https://github.com/rbuckton/ReflectDecorators), a prototype for an ES7 Reflection API for Decorator Metadata.
+```ts
+@jsonObject
+class MyDataClass
+{
+    // Primitives serialization
+    @jsonMember
+    public prop1: number; // or string, boolean, etc.
 
- - Parse regular JSON to typed class instances, safely
- - Seamlessly integrate into existing code with [decorators](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md), ultra-lightweight syntax
+    // Array serialization
+    @jsonArrayMember(Number)
+    public arrayProp: number[];
 
-## Install & Use
+    // Map serialization
+    @jsonMapMember(Number, String)
+    public mapProp: Map<number, string>;
 
-```none
-npm install typedjson-npm
-typings install npm:typedjson-npm
+    // Set serialization
+    @jsonSetMember(Number)
+    public setProp: Set<number>;
+}
 ```
 
- 1. Snap the [@JsonObject decorator](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonobject) on a class
- 2. Snap the [@JsonMember decorator](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember) on properties which should be serialized and deserialized
- 3. Parse and stringify with the [TypedJSON class](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#typedjson)
+Of course, all 4 serialization techniques (single, array, map, set) support nested objects (nested object class must be also decorated with `@jsonObject` for this to work, obviously). Example:
 
-```typescript
-@JsonObject
-class Person {
-    @JsonMember
-    firstName: string;
+```ts
+@jsonObject
+class MySecondDataClass
+{
+    @jsonMember
+    public prop1: number;
 
-    @JsonMember
-    lastName: string;
+    @jsonMember
+    public prop2: number;
+}
 
-    public getFullname() {
-        return this.firstName + " " + this.lastName;
-    }
+@jsonObject
+class MyDataClass
+{
+    @jsonMember
+    public prop1: MySecondDataClass;
+    
+    @jsonArrayMember(MySecondDataClass)
+    public arrayProp: MySecondDataClass[];
+
+    @jsonMapMember(Number, MySecondDataClass)
+    public mapProp: Map<number, MySecondDataClass>;
 }
 ```
 
-```typescript
-var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);
+Additionally, there's built-in support for TypedArray objects (serialized as `number[]`), `Date`, `ArrayBuffer` (serialized as string at this time, so this might not be a good idea, prefer using a TypedArray instead), this is available by simply using `@jsonMember`. Serialization of Maps, Sets, and Arrays of root objects is also supported.
 
-person instanceof Person; // true
-person.getFullname(); // "John Doe"
-```
+After annotating your objects as shown above, you simply consume them by creating a new `TypedJSON` object, supplying the _constructor_ of the root data type to it:
 
-If you choose to omit using [ReflectDecorators](https://github.com/rbuckton/ReflectDecorators), the class (constructor function) of each [@JsonMember](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember) decorated property must be specified manually through the [`type` setting](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference#jsonmember), for example:
+```ts
+let object = new MyDataClass(); // ...
+let serializer = new TypedJSON(MyDataClass);
 
-```typescript
-@JsonMember({ type: String })
-firstName: string;
+let json = serializer.stringify(object);
+let object2 = serializer.parse(json);
 ```
 
-[Learn more about decorators in TypeScript](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md)
+**How are these objects serialized?**
+
+Sets and arrays are simply serialized as arrays, Maps are serialized as arrays of _key-value-pair objects_, TypedArrays are serialized as numeric arrays.
 
-## Documentation
+**How to use without reflect-metadata?**
 
- - [API reference](https://github.com/JohnWhiteTB/TypedJSON/wiki/API-reference)
+If you don't use `reflect-metadata`, you need to manually add the constructor reference to `@jsonMember`, e.g.:
 
-## License
+```diff
+@jsonObject
+class MyDataClass
+{
+-   @jsonMember
++   @jsonMember({ constructor: Number })
+    public prop1: number;
+
+-   @jsonMember
++   @jsonMember({ constructor: MySecondDataClass })
+    public prop2: MySecondDataClass;
+}
+```
 
-TypedJSON is licensed under the MIT License.
+This is not needed for `@jsonArrayMember`, `@jsonMapMember`, and `@jsonSetMember`, as those types already know the property type itself, as well as element/key types (although using `reflect-metadata` adds runtime-type checking to these decorators, to help you spot errors).

Move readme from new version