npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ultimicro/json-mapper

v1.2.0

Published

TypeScript decorators for mapping between JSON and Classes

Downloads

1

Readme

TypeScript decorators for mapping between JSON and Classes

npm (scoped)

This is a powerful utility to convert and validate JSON value for TypeScript. The main purposes of this library is to enforce JSON schemas and make it able to use instanceof to determine the type of JSON value.

The original source code of this package came from https://github.com/GillianPerard/typescript-json-serializer

Runtime requirements

Required compiler options

  • experimentalDecorators
  • emitDecoratorMetadata

Basic usages

Create model

Create a model to represent a JSON object:

import { JsonClass, JsonProperty } from '@ultimicro/json-mapper';

@JsonClass()
class Foo {
  @JsonProperty()
  v1: string;

  @JsonProperty({ type: Number }) // nullable type required to specify type explicitly
  v2: number | null;

  @JsonProperty({ args: [String] })
  v3: string[];

  @JsonProperty({ args: [Date] })
  v4: Map<string, Date>;

  @JsonProperty({ optional: true })
  v5?: Date;

  constructor(v1: string, v2: number | null, v3: string[], v4: Map<string, Date>) {
    this.v1 = v1;
    this.v2 = v2;
    this.v3 = v3;
    this.v4 = v4;
  }
}

Convert JSON to the model instance

import { fromJSON } from '@ultimicro/json-mapper';

const json = JSON.parse('{"v1": "abc", "v2": 123, "v3": ["foo"], "v4": "2006-01-02T15:04:05.000Z"}');
const model = fromJSON(json, Foo);

Convert the model instance to JSON

import { toJSON } from '@ultimicro/json-mapper';

const model = new Foo('abc', 123, ['foo']);
const str = toJSON(model);
const obj = toJSON(model, false); // invoke JSON.stringify(obj) to get JSON string

Advanced usage

Map a class as single value instead of object

import { InvalidProperty, JsonArray, JsonClass, JsonObject, JsonScalar, MappingContext } from '@ultimicro/json-mapper';

@JsonClass({ reader: readBar, writer: writeBar })
class Bar {
  constructor(readonly value: string) {
  }
}

function readBar(ctx: MappingContext, json: JsonScalar | JsonObject | JsonArray): Bar {
  if (typeof json !== 'string') {
    throw new InvalidProperty(`Expect string, got ${typeof json}.`, ctx.currentPath());
  }

  return new Bar(json);
}

function writeBar(ctx: MappingContext, obj: Bar): JsonScalar | JsonObject | JsonArray {
  return obj.value;
}

Using 3rd party classes as a model

import { configClass, configProperty } from '@ultimicro/json-mapper';
import { SomeClass } from 'somelib';

// the bottom code MUST run exactly one
configClass(SomeClass); // use the second argument to specify custom reader/writer to treat this class as a single value like the above example
configProperty(SomeClass, { name: 'prop1', type: String }); // you can use any additional options that are available on JsonProperty

// now you can use SomeClass as a JSON model anywhere

Property with dynamic type

import { InvalidProperty, JsonClass, JsonProperty, JsonValue, MappingContext, Type } from '@ultimicro/json-mapper';

@JsonClass()
class Foo {
  @JsonProperty({ discriminator: getValueType })
  v1: string | number | null;

  constructor(v1: string | number | null) {
    this.v1 = v1;
  }
}

function getValueType(ctx: MappingContext, obj: Foo, json: JsonValue): Type | { type: Type, required?: boolean } {
  if (json === null) {
    return null;
  }

  // you can access all PREVIOUS properties of your class here
  switch (typeof json) {
    case 'string':
      return String;
    case 'number':
      return Number;
    default:
      throw new InvalidProperty(`Unknown value ${typeof json}.`, ctx.currentPath());
  }
}

Polymorphism support

Polymorphism work by constructing a base object then invoke getType after mapping is completed to get a constructor of the real value, which will get invoked afterward and map all remaining properties. Then the properties of the base object will be moved to the real value except if it is marked with movable: false:

import { Constructor, GenericClass, InvalidProperty, JsonClass, JsonProperty, MappingContext, PolymorphismObject } from '@ultimicro/json-mapper';

const enum ValueType {
  Foo = 0,
  Bar = 1
}

@JsonClass()
abstract class Base implements PolymorphismObject {
  constructor(type: ValueType) {
    this.type = type;
  }

  getType(ctx: MappingContext): Constructor | GenericClass {
    switch (this.type) {
      case ValueType.Foo:
        return Foo;
      case ValueType.Bar:
        return Bar;
      default:
        throw new InvalidProperty(`Unknown type ${this.type}.`, ctx.pathFor('type'));
    }
  }

  @JsonProperty({ movable: false }) // we don't need to move this value due to the derived class explicitly assign it via constructor
  private type: ValueType;
}

@JsonClass()
class Foo extends Base {
  @JsonProperty()
  v1: string;

  constructor(v1: string) {
    super(ValueType.Foo);
    this.v1 = v1;
  }
}

@JsonClass()
class Bar extends Base {
  @JsonProperty()
  v1: number;

  constructor(v1: number) {
    super(ValueType.Bar);
    this.v1 = v1;
  }
}

Generic class support

import { JsonClass, JsonProperty } from '@ultimicro/json-mapper';

@JsonClass()
class Foo<T1, T2> {
  @JsonProperty({ type: 0 })
  v1: T1;

  @JsonProperty({ type: 1, required: false })
  v2: T2 | null;

  constructor(v1: T1, v2: T2 | null) {
    this.v1 = v1;
    this.v2 = v2;
  }
}

@JsonClass()
class Bar {
  @JsonProperty({ args: [String, Number] })
  v1: Foo<string, number>;

  @JsonProperty({ args: [{ type: String, required: false }, Number] })
  v2: Foo<string | null, number>;

  constructor(v1: Foo<string, number>, v2: Foo<string | null, number>) {
    this.v1 = v1;
    this.v2 = v2;
  }
}

Development

Running unit tests

npm test