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
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.
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
`
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
.
The test is very very simple, you just have to clone the project, do npm install
and run npm run test
, nothing special.
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
`
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 |
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!
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
`
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.
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.
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
`
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
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" }
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 ${}
.
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"
}
}
*/
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',
},
])
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 =,="
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"
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.
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. |
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. |
- Aditya Kresna Permana - Indonesia - SlaveOfCode
This project is licensed under the MIT License - see the LICENSE.md file for details
- 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.