JKT Parser
Simple helper to parse your JSON.
UPDATE: Take a look at Podeng
a similar parser like JKT with richer features, go visit the project here
-
JKT Parser
- Background
- Running the tests
-
API References
- Struct
- Available Types
- Instance of Struct
- One Line vs Multi Line
- Custom Predefined Value
- Extending Struct
- Removing Parent Property
- Check The Instance and Child
- Strict Types
- ENUM Value
- Nested Struct
- Array Container
- Custom Value Translator
- Arrow Mapping Key -> Values
- Struct & Instance References
- Struct Property & Function
- Instance Function
- Author
- License
- Acknowledgments
Background
At the first time I wonder how could I make my JSON to be more manageable. So confusing when every time I checking up the right parameters to my function, make sure the produced JSON data are valid and parsing all over the JSON properties to follow my rules (types).
Then I do research and no one module is available to fit in with my case, so I built this one.
JKT is a simple Javascript module to create a structure for your JSON. It's basically just a simple parser to handle property types, the structure and provide a small helper to handle the data.
Requirements
To use JKT you need a NodeJS version 6.4.0
and up. Basically JKT really depends on ES6 style which using template literal in practice.
const jkt = require('jkt') // CommonJs
const Person = jkt`
name: String
age: Number
birthday: Date
hobbies: Array
`
Installing
As described before, you need NodeJs with version 6.4.0
and up before using this module. After that installing JKT is just simply as.
Using NPM
npm i jkt --save
Using Yarn
yarn add jkt
When finished you'll notice that this modules requires some libraries like lodash
, moment
and shortid
.
Running the tests
The test is very very simple, you just have to clone the project, do npm install
and run npm run test
, nothing special.
API References
Struct
You just have to define the JKT struct once and then you could rely on them. The struct is defined by using template literal.
const Person = jkt`
name: String
age: Number
birthday: Date
Hobbies: Array
`
Available Types
Type | Description | Show on Invalid? | Show on JSON result |
---|---|---|---|
String |
String type value | Yes | Yes |
String! |
Force to only accept string value | No | No |
Number |
Numeric type value, works for either Integer or Float | Yes | Yes |
Number! |
Force to only accept numeric value | No | No |
Boolean |
Boolean type value, works for either Integer or Float | Yes | Yes |
Boolean! |
Force to only accept boolean value | No | No |
Date |
Date type value that accept ISO 8601 , supported by Moment and it is timezone aware (will convert to UTC time) based on your machine timezone |
Yes | Yes |
Date! |
Force to only accept valid date value and will produce timezone aware date | No | No |
DatePlain |
Date type value that accept ISO 8601 , supported by Moment and it is not timezone aware |
Yes | Yes |
DatePlain! |
Force to only accept valid date value and will not produce timezone aware date | No | No |
Array |
Array type value | Yes | Yes |
Array! |
Force to only accept array value | No | No |
Object |
Object type value | Yes | Yes |
Object! |
Force to only accept object value | No | No |
Function |
Function type value | No | No |
Function! |
Force to only accept function | No | No |
ANY |
Any type value will be valid | Yes | Yes |
Instance of Struct
You can assume the Person
as a structure for json data, then every time you do parsing, you just have to pass an argument into Person
.
const aditya = Person({
name: "Aditya Kresna",
age: '26',
birthday: '1991-06-18' // ISO 8601
})
// now aditya is the instance of Person
Then you could use aditya
properties or produce valid JSON format from it
aditya.name // "Aditya Kresna"
aditya.birthday // moment time
aditya.toJSON() // produce valid json format
aditya.j() // the shorthand method
One thing that you should know is if JKT fails to identify type of the value, it will returning null
as a default, except you use force type like String!
and Number!
One Line vs Multi Line
There is a few method you can follow while making a struct, One Line and Multi Line. If you think your struct object is short and don't wanna make more space with using multi lines, you could simply create a struct separated by comma ,
.
const Animal = jkt`type: String, color: String, isWild: Boolean`
or by multiple lines like this
const Animal = jkt`
type: String
color: String
isWild: Boolean
`
const Animal2 = jkt`
type: String,
color: String,
beast: Boolean
`
const Animal3 = jkt`
type: String!,
color: String!,
beast: Boolean
`
Custom Predefined Value
When you need to setup custom value upfront, without checking it's type or doing some validations, You could use it as a predefined value by put them inside of expression ${}
. Predefined value is the value you define when defining the struct.
const Mother = jkt`
name: String
birthday: Date
haveChild: ${true}
`
const angela = Mother({
name: "Angela",
Birthday: "1990-06-06"
})
const christy = Mother({
name: "Angela",
Birthday: "1990-06-06",
haveChild: false
})
const Person = jkt`
name: String
sayTheWords: ${(words) => `Hi, ${words}`},
someOptions: ${{some: "options"}}
`
const aditya = Person({
name: "Aditya"
})
angela.haveChild // true
christy.haveChild // false
aditya.sayTheWords('How are you') // "Hi, How are you"
aditya.j()
// { name: "Aditya", someOptions: { some: "options" } }
You could pass anything you want, but if you pass a function
for example, it will not showing on the output when you calling toJSON
or j
function, because the value wasn't a valid JSON type.
Extending Struct
Once you define a struct it possible to extend into another struct.
const Person = jkt`
name: String
age: Number
hobby: Array
birthday: Date
`
const Driver = Person`
useBike: Boolean
useCar: Boolean
`
const Doctor = Person`
specialist: String
hospitalLocation: String
`
Both of Driver
and Doctor
is a child of Person
, so you will get the name
, age
, hobby
and birthday
properties when you do parse of the driver
and doctor
instance.
Removing Parent Property
Sometimes we want to extend from existing struct but on a few situation we don't wanna include some properties. By using !DELETE
we can exclude the property when extending from existing struct.
const Person = jkt`
name: String
age: Number
hobby: Array
drinkBeer: Boolean
`
const Child = Person`
toys: Array
drinkBeer: !DELETE // this "drinkBeer" will be deleted on child struct
`
Check The Instance and Child
It is also possible to checking the instance and child.
const Person = jkt`
name: String
age: Number
hobby: Array
`
const Child = Person`
toys: Array
doingHomework: Boolean
`
const Mother = Person`
singleParent: Boolean
`
const John = Child({
name: "John Doe"
})
Child.childOf(Person) // true
Mother.childOf(Person) // true
John.instanceOf(Person) // true
John.instanceOf(Child) // true
Strict Types
As mentioned before (on a table), every unsupplied value or invalid type would make the property have null
value when parsed. But we can force the property to not exist when invalid value raised.
const Person = jkt`
name: String
age: Number!
hobby: Array!
`
const John = Person({ name: "John Doe", age: "not sure"})
John.j() // { name: "John Doe" }
ENUM Value
We often need to reference some value based on it's own defined types. This could be done with ENUM
, where ENUM is a feature when we need some property to strictly follow the type as we defined on ENUM itself.
const Colors = jkt.ENUM`
RED: Maroon
WHITE
BLUE: ${'just blue'}
`
const TSize = jkt.ENUM`small, medium, large, extra_large: ${'EXTRA-LARGE'}`
const TShirt = jkt`
model: String
brand: Number!
color: ${Colors}
size: ${TSize}
`
// Calling enum directly
Colors() // { RED: 'Maroon', WHITE: 'WHITE', BLUE: 'just blue' }
TSize() // { SMALL: 'SMALL', MEDIUM: 'MEDIUM', LARGE: 'LARGE', EXTRA_LARGE: 'EXTRA-LARGE' }
// Callling enum from struct using "E" property
TShirt.E.COLOR.RED // "Maroon"
The E
stands for the collection of the ENUM on TShirt
. If you want to see complete values of ENUM just take the E
property.
All enum properties and value would be converted to Upper-Case string (even if it's a number), it doesn't accept any special characters except underscore _
and If you want to set custom value just use an expression ${}
.
Nested Struct
Every single struct we define is an independent structure that could be used with another struct. By this point you got a very reusable component as you may need the same structure on another struct (eg. as a collection).
const Person = jkt`
name: String
age: Number
birthday: Date
`
const SchoolClass = jkt`
name: String
grade: Number
teacher: ${Person}
`
// show the schema
SchoolClass.schema
/**
{
name: "String",
grade: "Number",
teacher: {
name: "String",
age: "Number",
birthday: "Date"
}
}
*/
const mySchoolClass = SchoolClass({
name: 'Awesome Class',
grade: '10',
teacher: {
name: 'Amelia',
age: 25,
birthday: '1992-05-31' // ISO 8601
}
})
mySchoolClass.j()
/**
{ name: "Awesome Class",
grade: 10,
teacher: {
name: "Amelia",
age: 25,
birthday: "1992-05-30T17:00:00.000Z"
}
}
*/
Array Container
Container to keep our struct inside json array.
const Person = jkt`
name: String
age: Number
birthday: Date
`
const SchoolClass = jkt`
name: String
grade: Number
students: ${jkt.c.array(Person)}
`
const strictNull = false // if some value has null, the item will not added into list
const defaultToArray = true // set default value to array ( not null )
const People = jkt.array(Person, strictNull, defaultToArray)
const listOfPeople = People([
{
name: 'Aditya',
age: '27',
birthday: '1991-06-18',
},
{
name: 'John',
age: '20',
birthday: '1996-10-04',
},
])
Custom Value Translator
With all of provided parsers, I believe there is always not enough to cover up our desired types. So here's translator comes in.
const Person = jkt`
name: String
age: Number
birthday: ${jkt.trans.custom(val => "I'm old man =,=")}
`
const nina = Person({
name: "Nina",
age: "25",
birthday: new Date() // this will produce "I'm old man =,="
});
nina.birthday // "I'm old man =,="
nina.j().birthday // "I'm old man =,="
Arrow Mapping Key -> Values
With mapping you could reuse key-value on supplied json to make your own custom key based on that source.
const Person = jkt`
name: String
name->full_name: String // mapping from source key (name) to new key (full_name)
address: String
address->address2: String // mapping from source key (address) to new key (address2)
age: Number
age->ageInString: String // mapping from source key (age) to new key (ageInString) with type String
`
const aditya = Person({
name: "Aditya",
age: "27",
address: "Kota Bekasi"
});
aditya.name // "Aditya"
aditya.full_name // "Aditya"
aditya.address2 // "Kota Bekasi"
aditya.age // 27
aditya.ageInString // "27"
Struct & Instance References
These are detailed function & properties you could use when using jkt struct. You shouldn't use the following reserved words as a property for your struct, because it was reserved to support the module.
Struct Property & Function
Name | Type | Description |
---|---|---|
isJKT |
Boolean (true) | Nothing special about this, just used to to identify JKT struct on internal parser. |
schema |
JSON | Schema of struct, you could inspect this property after defining the struct. |
childOf |
Function | To check if the struct is a child of the given struct |
__id |
String | The id of struct, every struct has an unique id generated based on shortid library |
__schema |
JSON | The dirty schema of the struct which used internally to parse value |
E |
JSON | A container of all enum values on the struct, this property only available when we set some property with ENUM type. |
Instance Function
Name | Description |
---|---|
instanceOf |
To identify the instance of struct |
getSchema |
To get struct schema from the instance |
getDirtySchema |
To get the real struct schema from the instance. The result including the function and properties to do parse inside the module |
toJSON |
To get valid json from the instance. |
j |
To get valid json from the instance. This is a shorthand method of toJSON
|
toString |
To get json string from the instance. |
Author
- Aditya Kresna Permana - Indonesia - SlaveOfCode
License
This project is licensed under the MIT License - see the LICENSE.md file for details
Acknowledgments
- This module may have some limitation based on reserved naming of the methods and properties
- Highly inspired with styled-components style
- This module may still buggy, make a pull request or make an issue if you found them.