You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

json-decoder

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-decoder - npm Package Compare versions

Comparing version

to
0.2.0

dist/decoder.js

5

package.json
{
"name": "json-decoder",
"version": "0.1.0",
"version": "0.2.0",
"description": "",

@@ -27,3 +27,2 @@ "main": "index.js",

"@types/jest": "^24.0.15",
"babel-jest": "^24.8.0",
"jest": "^24.8.0",

@@ -37,2 +36,2 @@ "rollup": "^1.16.6",

"dependencies": {}
}
}

141

README.md

@@ -7,10 +7,147 @@ # TypeScript JSON Decoder: `json-decoder`

## Compositional
## Compositional decoding
The decoder comprises of small basic building blocks (listed below), that can be composed into JSON decoders of any complexity, including deeply nested structures, heterogenous arrays, etc. If a type can be expressed as `TypeScript` `interface` or `type` (including algebraic data types) - it can be safely decoded and type checked with `json-decoder`.
## Basic decoders
## Monadic result
Below is a list of basic decoders supplied with `json-decoder`:
- `stringDecoder` - decodes a string:
```
let result: Result<string> = stringDecoder.decode("some string"); //Ok("some string");
let result: Result<string> = stringDecoder.decode(123.45); //Err("string expected");
```
- `numberDecoder` - decodes a number:
```
let result: Result<number> = numberDecoder.decode(123.45); //Ok(123.45);
let result: Result<number> = numberDecoder.decode("some string"); //Err("number expected");
```
- `boolDecoder` - decodes a boolean:
```
let result: Result<boolean> = boolDecoder.decode(true); //Ok(true);
let result: Result<boolean> = boolDecoder.decode(null); //Err("bool expected");
```
- `nullDecoder` - decodes a `null` value:
```
let result: Result<null> = nullDecoder.decode(null); //Ok(null);
let result: Result<null> = boolDecoder.decode(false); //Err("null expected");
```
- `undefinedDecoder` - decodes an `undefined` value:
```
let result: Result<null> = nullDecoder.decode(undefined); //Ok(undefined);
let result: Result<null> = boolDecoder.decode(null); //Err("undefined expected");
```
- `arrayDecoder<T>(decoder: Decoder<T>)` - decodes an array, requires one parameter of array item decoder:
```
let result: Result<number[]> = arrayDecoder.decode([1,2,3]); //Ok([1,2,3]);
let result: Result<number[]> = arrayDecoder.decode("some string"); //Err("array expected");
let result: Result<number[]> = arrayDecoder.decode([true, false, null]); //Err("array: number expected");
```
- `objectDecoder<T>(decoderMap: DecoderMap<T>)` - decodes an object, requires a decoder map parameter. Decoder map is a composition of decoders, one for each field of an object, that themselves can be object decoders if neccessary.
```
type Pet = {name: string, age: number};
let petDecoder = objectDecoder<Person>({
name: stringDecoder,
age: numberDecoder,
});
let result: Result<Pet> = petDecoder.decode({name: "Varia", age: 0.5}); //Ok({name: "Varia", age: 0.5});
let result: Result<Pet> = petDecoder.decode({name: "Varia", type: "cat"}); //Err("name: string expected");
let petDecoder = objectDecoder<Person>({
name: stringDecoder,
type: stringDecoder, //<-- error: field type is not defined in Pet
});
```
- `exactDecoder<T>(value: T)` - decodes a value that is passed as a parameter. Any other value will result in `Err`:
```
let catDecoder = exactDecoder("cat");
let result: Result<"cat"> = catDecoder.decode("cat"); //Ok("cat");
let result: Result<"cat"> = catDecoder.decode("dog"); //Err("cat expected");
```
- `oneOfDecoders<T1|T2...Tn>(...decoders: Decoder<T1|T2...Tn>[])` - takes a number decoders as parameter and tries to decode a value with each in sequence, returns as soon as one succeeds, errors otherwise. Useful for algebraic data types.
```
let catDecoder = exactDecoder("cat");
let dogDecoder = exactDecoder("dog");
let petDecoder = oneOfDecoders<"cat"|"dog"> = oneOfDecoders(catDecoder, dogDecoder);
let result: Result<"cat"|"dog"> = petDecoder.decode("cat"); //Ok("cat");
let result: Result<"cat"|"dog"> = petDecoder.decode("dog"); //Ok("dog");
let result: Result<"cat"|"dog"> = petDecoder.decode("giraffe"); //Err("none of decoders matched");
```
- `allOfDecoders(...decoders: Decoder<T1|T2...Tn>[]): Decoder<Tn>` - takes a number decoders as parameter and tries to decode a value with each in sequence, all decoders have to succeed. If at leat one defocer fails - returns `Err`.
```
let catDecoder = exactDecoder("cat");
let result: Result<"cat"> = allOfDecoders(stringSecoder, catDecoder); //Ok("cat")
```
## API
Each decoder has the following methods:
- `decode(json:unknown): Result<T>` - attempts to decode a value of `unknown` type. Returns `Ok<T>` if succesful, `Err<T>` otherwise.
- `decodeAsync(json:unknown): Promise<T>` - Returns a `Promise<T>` that attempts to decode a value of `unknown` type. Resolves with `T` if succesful, rejects `Error{message:string}` otherwise.
A typical usage of this would be in an `async` function context:
```
const getPet = async (): Promise<Pet> => {
const result = await fetch("http://some.pet.api/cat/1");
const pet:Pet = await petDecoder.decodeAsync(await result.json());
return pet;
};
```
- `map(func: (t:T) => T2) : Decoder<T2>` - each decoder is a [functor](https://wiki.haskell.org/Functor). `Map` allows you to apply a function to an underlying deocoder value, provided that decoding succeeded. Map accepts a function of type `(t:T) -> T2`, where `T` is a type of decoder (and underlying value), and `T2` is a type of resulting decoder.
- `then(bindFunc: (t:T) => Decoder<T2>): Decoder<T2>` - allows for [monading](https://wiki.haskell.org/Monad) chaining of decoders. Takes a function, that returns a `Decoder<T2>`, and returns a `Decoder<T2>`
### Custom decoder
## Result and pattern matching
Decoding can either succeed or fail, to denote that `json-decoder` has [ADT](https://en.wikipedia.org/wiki/Algebraic_data_type) type `Result<T>`, which can take two forms:
- `Ok<T>` - carries a succesfull decoding result of type `T`, use `.value` to access value
- `Err<T>` - carries an unsuccesfull decodign result of type `T`, use `.message` to access error message
`Result` also has functorial `map` function that allows to apply a function to a value, provided that it exists
```
let r:Result<string> = Ok("cat").map(s => s.toUpperCase); //Ok("CAT")
let e:Result<string> = Err("some error").map(s => s.toUpperCase); //Err("some error")
```
It is possible to pattern-match (using poor man's pattern matching provided by TypeScript) to determite the type of `Result`
```
// assuming some result:Result<Person>
switch (result.type) {
case OK: result.value; // Person
case Err: result.message; // message string
}
```
## Friendly errors
## Mapping and type conversion
## Validation
import ts from "rollup-plugin-typescript";
export default {
input: "src/index.ts",
input: "src/decoder.ts",
output: {
format: "cjs",
file: "build/bundle.js",
file: "dist/decoder.js",
sourcemap: true

@@ -9,0 +9,0 @@ },