json-express
Parse JSON expression with string templates and transform it into another forms that can be a XML data, React component, etc.
Install
npm install json-express --save
Example
See basic example on CodeSandbox.
Usage
; const je = ; je;
The first argument of je.build
is an expression that needs to be processed, and second argument is a context from where the builder searches the values of its given variables. The third argument is not shown above but it's a callback to be called when intermediate results and final result are processed.
je.build
returns a promise that resolves when final result processed. Intermediate results are also processed if placeholders are there but only final result resolves.
TemplateExpression
JsonExpress has a built-in string template system named TemplateExpression. So every string element parsed by JsonExpress will be processed within its context.
You can get TemplateExpression object directly from JsonExpress.Template
. And don't forget its result will always be a Promise object.
TemplateExpression takes two types of expression: ReturningString and TemplateString.
ReturningString is to get expression result directly without converting it to string. ReturningString has only one form like "{=expresion}"
.
And TemplateString is to concatenate all its expression results into one string. TemplateString can include a number of template expressions with literal text like: "This is {expression1} and {expression2}"
.
Examples
const context = foo: 7 user: name: 'Dongho' age: 29 ;
Expression | Result |
---|---|
"{=foo}" |
7 |
"{=user.name}" |
"Dongho" |
"Name: {user.name}, Age: {user.age}" |
"Name: Dongho, Age: 29" |
Pipes
Pipes can be used with the template expressions and consume its result as an argument and return another result. Let's get an example:
"{=user.name | upperCase}"
The result would be: "DONGHO"
.
And pipes can be followed by another like this:
"{=user.name | upperCase | truncate 6}"
The result would be: "DON..."
You can add your custom pipes by JsonExpress.Template.addPipeHandler
.
JsonExpressTemplate;
More arguments not only can be given, also it can be asynchronous.
JsonExpressTemplate;
And there are built-in pipe handlers:
Pipe Name | Example |
---|---|
byte | {1024 \| byte} -> "1kb" |
percentage | {0.8 \| percentage} -> "80%" |
currency | {1024 \| currency} -> "1,024" |
byteLength | {"abc" \| byteLength} -> 3 |
pad | {32 \| pad 5} -> "00032" |
truncate | {"this is long" \| truncate 5} -> "this…" |
upperCase | {"CamelCase" \| upperCase} -> "CAMELCASE" |
lowerCase | {"CamelCase" \| lowerCase} -> "camelcase" |
date | {"2019-07-30T10:50:12.049Z" \| date "YYYY/MM/DD"} -> "2019/07/30" |
timeAgo | {"2019-07-30T10:50:12.049Z" \| timeAgo} -> "today" |
Object Builder
The constructor of JsonExpress
takes an array of builders.
The builder contains schema
and build
properties.
schema
property is an object that determines whether it can process or not. If the schema object matches the given JSON data, builder processes with its build
callback. And build
gets JSON data of which all properties are processed already.
And the builder can have optional placeholder
callback. Actually, build
callback can be async function, and instead of waiting for the async result, JsonExpress uses the result of placeholder
.
Thus, JsonExpress may return a result several times repeatedly because placeholder
changes the result whenever async build
returns. So you need to take the result from third argument of JsonExpress.build
method:
const je = ...; je;
The builder also has more optional properties: name
and exclusive
.
name
is an optional property and it can be used by buildType
property of the schema column that explained later.
exclusive
is also an optional property. You can set it true if you don't want it for global schema matching and then you can use it on buildType
property of the schema column by using the name
property.
Schema
A schema object can be defined like below:
name: type: 'string' value: 'Dongho' age: type: 'number' required: false rest: rest: true required: false
And it matches:
name: 'Dongho' age: 29
name: 'Dongho' foo: true
Not matches:
name: 'Jonghyeon'
Schema Column
A schema column is a property of a schema object and can have optional properties.
There are three state of the object in the object building process. I call the source
that is the very first object you give, and the processed object
in which its properties are all processed by the building process but not itself, so it is a plain object yet. And lastly, the built object
is the final output of the building process, but it can be a property value of another object if it is not the root.
Property | Description |
---|---|
type | Type checking is occured for the processed object not for the source. So it checks its type of the processed value at runtime. The type property accepts string by default though, you can customize the handling of type checking easily. |
buildType | You can control its build process by setting buildType . Set to "string", then its build process regards it as a string even though it's not a string, in that case it will throw an error. Custom types also can be choosen like "CustomType1 | CustomType2", in which the pipe \| here means "or". CustomType1 and CustomType2 are set by the builder name. |
value | The value property can be a RegExp or any primitive value like a string. It's for the source unlike the type property, so it can match to the proper builder. |
Deprecated. Use buildType instead. |
|
rest | If it's true then it gathers the rest properties that are not declared explicitly in the schema and make it into one independent object. |
required | Set if this column is required. It's true by default. |
lazy | Instead of giving value directly, it gives an async function that will put the processed value to the place you want. Its lazy function accepts a context as an argument. The type checking also occurs when lazy function resolves a value. |
Type Check
At the first version of JsonExpress, the type of schema columns was evaluated in the schema matching. But it turns out that it's meaningless defining all the types as any
in the end, since the type of the column value changes along with the buliding process.
So the type
property becomes to use the runtime type checker. The default built-in type checker is same as used in buildType checker. You can replace it with another type checker like Ajv or Yup by assigning to JsonExpress.typeCheckerGenerator
:
; JsonExpress { return { type; };};
Are type checkings asynchronous, you can catch type errors and other asynchronous errors from the callback given by third argument of JsonExpress.build
method:
const je = ...; je;
Build Type
The buildType
property of the schema column accepts a string that represents Javascript types. A simple representation can be like "string"
or "number[]"
. But more complex types can be represented:
Type representation | Description | Matching Example |
---|---|---|
string | A string type. | "Hello world!" |
number | A number type. | 123 |
boolean | A boolean type. | true |
array | An array type. | [1, true, "aray"] |
null | A null type. | null |
any | Any types including custom types. | "Hello" , 123 , [] |
string[] | An array type in which all values are of a string type. | ["Hello", "world"] |
[string, boolean] | A tuple type that first item is a string type and second item is a boolean type. | ["Hello", true] |
{ name: string, age?: number, ...: any } | An object that name property is a string type, age property is an optional number type and rest properties are all any type. |
{ name: "Jonghyeon", country: "Korea" } |
string | number | A string type or a number type. | "Hello world!" , 123 |
(string | number)[] | An array type in which all values are one of a string type or a number type. | ["Hello", 123] |
The arrow representation can be used for set both buildType
and type
properties like:
buildType: "string[] -> number[]" ...
The first type before ->
will set buildType
property as string[]
and the second type after ->
will set type
property as 'number[]'. So above representation is equal to:
type: "number[]" buildType: "string[]" ...
Unless you set your own type checker that doesn't understand such a representation, it might be useful as a shortcut.
License
Licensed under the MIT license.