deneric2
Version:
JSON parser
493 lines (430 loc) • 17 kB
text/typescript
import { it, describe } from 'mocha'
import { expect } from 'chai'
// import Deneric, { DenericSchema } from '../dist/deneric'
import Deneric, { DenericSchema } from '../src/Deneric'
const STUDENT_SCHEMA: DenericSchema = {
full_name: ['profile.full_name', String],
age: ['profile.age', Number],
is_male: ['others.is_male', Boolean],
roles: ['others.roles', Deneric.Array(String)],
jobs: ['jobs', Deneric.Map(Deneric.Array(String))],
highscores: ['highscores', Deneric.Array(Number)]
}
class Student extends Deneric {
full_name: string = 'noname'
age: number = -1
is_male: boolean = false
roles: (string | number)[] = ['100']
jobs: { [key: string]: string[] } = { 2023: ['2023'] }
highscores!: number[]
constructor(data: any) {
super(STUDENT_SCHEMA)
this.fromJson(data)
}
}
class StudentIgnoreJob extends Deneric {
full_name: string = 'noname'
age: number = -1
is_male: boolean = false
roles: (string | number)[] = ['100']
jobs: { [key: string]: string[] } = { 2023: ['2023'] }
constructor(data: any) {
super({
full_name: ['profile.full_name', String],
age: ['profile.age', Number],
is_male: ['others.is_male', Boolean],
roles: ['others.roles', Deneric.Array(String)],
jobs: ['jobs', Object, true],
})
this.fromJson(data)
}
}
class ClassRoom extends Deneric {
monitor: Student = new Student({})
students: Student[] = []
mapStudents: Record<string, Student> = {}
constructor() {
super({
monitor: ['class_monitor', Student],
students: ['my_student', Deneric.Array(Student)],
mapStudents: ['map_student', Deneric.Map(Student)]
})
}
}
class InvalidStudent extends Deneric {
constructor() {
super(null as unknown as DenericSchema);
}
}
class NoStrictClass extends Deneric {
number!: number
string!: string
boolean!: boolean
object!: object
array!: string[]
constructor() {
super({
number: ['number', Number],
string: ['string', String],
boolean: ['boolean', Boolean],
object: ['object', Object],
array: ['array', Array]
})
}
}
class ArrayNumber extends Deneric {
numbers!: number[]
constructor(data?: object) {
super({
numbers: ['', Deneric.Array(Number)]
})
this.fromJson(data)
}
}
const json1 = {
profile: {
full_name: 'John Smith',
age: 12
},
others: {
is_male: true,
roles: ['1', '2', '2a', '2b']
},
jobs: {
2021: ['A', 'B', 'C'],
2025: ['B', 'D']
},
highscores: [10, 9.5, 8.75]
}
const json2 = null
const json3 = {
profile: {
full_name: 1,
age: '12a'
},
others: {
is_male: 5,
roles: 'ABCDE'
},
jobs: true
}
const json4 = {
my_student: [json1, json2, json3],
class_monitor: json1,
map_student: {
json1,
json2,
json3
}
}
const noStrictJson = {
number: '123',
string: 21,
boolean: 'Not a Boolean',
object: true,
array: 'Not an Array'
}
describe('fromJson', () => {
it('checking schema instance', () => {
const r1 = new Student(json1)
const r2 = new Student(json1)
expect(Deneric.Utils.getSchema(r1)).to.be.equals(Deneric.Utils.getSchema(r2))
expect(Deneric.Utils.getSchema(r1)).to.be.equals(STUDENT_SCHEMA)
})
it('checking parse data from schema', () => {
const r = new Student(json1)
expect(r.full_name).to.be.eq(json1.profile.full_name)
expect(r.age).to.be.eq(json1.profile.age)
expect(r.is_male).to.be.eq(json1.others.is_male)
expect(r.roles).to.be.deep.equal(json1.others.roles)
expect(r.jobs).to.be.deep.equal(json1.jobs)
expect(r.highscores).to.be.deep.equal(json1.highscores)
})
it('checking parse default value (missing schema)', () => {
const r = new Student(null)
expect(r.full_name).to.be.eq('noname')
expect(r.age).to.be.eq(-1)
expect(r.is_male).to.be.eq(false)
expect(r.roles).to.be.deep.equal(['100'])
expect(r.jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(r.highscores).to.be.deep.equal([])
r.highscores = [1]
const r2 = r.clone<Student>()
expect(r2.highscores).to.be.deep.equal(r.highscores)
})
it('checking parse default value (wrong data type)', () => {
const r = new Student(json3)
expect(r.full_name).to.be.eq('noname')
expect(r.age).to.be.eq(-1)
expect(r.is_male).to.be.eq(false)
expect(r.roles).to.be.deep.equal(['100'])
expect(r.jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(r.highscores).to.be.deep.equal([])
})
it('checking default value when call fromJson', () => {
const r = new Student(json1)
expect(r.full_name).to.be.eq(json1.profile.full_name)
expect(r.age).to.be.eq(json1.profile.age)
expect(r.is_male).to.be.eq(json1.others.is_male)
expect(r.roles).to.be.deep.equal(json1.others.roles)
expect(r.jobs).to.be.deep.equal(json1.jobs)
r.fromJson(json3)
expect(r.full_name).to.be.eq('noname')
expect(r.age).to.be.eq(-1)
expect(r.is_male).to.be.eq(false)
expect(r.roles).to.be.deep.equal(['100'])
expect(r.jobs).to.be.deep.equal({ 2023: ['2023'] })
})
it('checking parse complex data type (Array, Map)', () => {
const r = new ClassRoom()
const r2 = r.fromJson<ClassRoom>(json4)
expect(r2.students.length).to.be.eq(json4.my_student.length)
expect(r.students[0].full_name).to.be.eq(json1.profile.full_name)
expect(r.students[0].age).to.be.eq(json1.profile.age)
expect(r.students[0].is_male).to.be.eq(json1.others.is_male)
expect(r.students[0].roles).to.be.deep.equal(json1.others.roles)
expect(r.students[0].jobs).to.be.deep.equal(json1.jobs)
expect(r.students[1].full_name).to.be.eq('noname')
expect(r.students[1].age).to.be.eq(-1)
expect(r.students[1].is_male).to.be.eq(false)
expect(r.students[1].roles).to.be.deep.equal(['100'])
expect(r.students[1].jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(r.students[2].full_name).to.be.eq('noname')
expect(r.students[2].age).to.be.eq(-1)
expect(r.students[2].is_male).to.be.eq(false)
expect(r.students[2].roles).to.be.deep.equal(['100'])
expect(r.students[2].jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(r.monitor.full_name).to.be.eq(json1.profile.full_name)
expect(r.monitor.age).to.be.eq(json1.profile.age)
expect(r.monitor.is_male).to.be.eq(json1.others.is_male)
expect(r.monitor.roles).to.be.deep.equal(json1.others.roles)
expect(r.monitor.jobs).to.be.deep.equal(json1.jobs)
expect(r.mapStudents['json1'].full_name).to.be.eq(json1.profile.full_name)
expect(r.mapStudents['json1'].age).to.be.eq(json1.profile.age)
expect(r.mapStudents['json1'].is_male).to.be.eq(json1.others.is_male)
expect(r.mapStudents['json1'].roles).to.be.deep.equal(json1.others.roles)
expect(r.mapStudents['json1'].jobs).to.be.deep.equal(json1.jobs)
expect(r.mapStudents['json2'].full_name).to.be.eq('noname')
expect(r.mapStudents['json2'].age).to.be.eq(-1)
expect(r.mapStudents['json2'].is_male).to.be.eq(false)
expect(r.mapStudents['json2'].roles).to.be.deep.equal(['100'])
expect(r.mapStudents['json2'].jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(r.mapStudents['json3'].full_name).to.be.eq('noname')
expect(r.mapStudents['json3'].age).to.be.eq(-1)
expect(r.mapStudents['json3'].is_male).to.be.eq(false)
expect(r.mapStudents['json3'].roles).to.be.deep.equal(['100'])
expect(r.mapStudents['json3'].jobs).to.be.deep.equal({ 2023: ['2023'] })
})
})
describe('toJson test', () => {
it('toJson default', () => {
const r = new Student(json1)
const json = r.toJson() as typeof json1
expect(json.profile.full_name).to.be.eq(json1.profile.full_name)
expect(json.profile.age).to.be.eq(json1.profile.age)
expect(json.others.is_male).to.be.eq(json1.others.is_male)
expect(json.others.roles).to.be.deep.equal(json1.others.roles)
expect(json.jobs).to.be.deep.equal(json1.jobs)
})
it('toJson missing schema', () => {
const r = new Student(json2)
const json = r.toJson() as typeof json1
expect(json.profile.full_name).to.be.eq('noname')
expect(json.profile.age).to.be.eq(-1)
expect(json.others.is_male).to.be.eq(false)
expect(json.others.roles).to.be.deep.equal(['100'])
expect(json.jobs).to.be.deep.equal({ 2023: ['2023'] })
})
it('toJson wrong data type', () => {
const r = new Student(json3)
const json = r.toJson() as typeof json1
expect(json.profile.full_name).to.be.eq('noname')
expect(json.profile.age).to.be.eq(-1)
expect(json.others.is_male).to.be.eq(false)
expect(json.others.roles).to.be.deep.equal(['100'])
expect(json.jobs).to.be.deep.equal({ 2023: ['2023'] })
})
it('toJson with valid default value', () => {
const n = new NoStrictClass()
n.fromJson(noStrictJson, false)
expect(typeof n.number === 'number')
expect(typeof n.string === 'string')
expect(typeof n.boolean === 'boolean')
expect(Array.isArray(n.array))
expect(typeof n.object === 'object')
let r: { number?: number, string?: string, boolean?: boolean, array?: string[], object?: object } = n.toJson()
expect(typeof r.number === 'number')
expect(typeof r.string === 'string')
expect(typeof r.boolean === 'boolean')
expect(Array.isArray(r.array))
expect(typeof r.object === 'object')
// @ts-ignore
n.number = undefined
// @ts-ignore
n.string = undefined
// @ts-ignore
n.boolean = undefined
// @ts-ignore
n.array = undefined
// @ts-ignore
n.object = undefined
r = n.toJson()
expect(typeof r.number === 'number')
expect(typeof r.string === 'string')
expect(typeof r.boolean === 'boolean')
expect(Array.isArray(r.array))
expect(typeof r.object === 'object')
})
it('toJson complex data type (Array, Map)', () => {
const r = new ClassRoom()
r.fromJson<ClassRoom>(json4)
const json = r.toJson() as typeof json4
expect(json.my_student.length).to.be.eq(json4.my_student.length)
expect((json.my_student[0] as typeof json1).profile.full_name).to.be.eq(json1.profile.full_name)
expect((json.my_student[0] as typeof json1).profile.age).to.be.eq(json1.profile.age)
expect((json.my_student[0] as typeof json1).others.is_male).to.be.eq(json1.others.is_male)
expect((json.my_student[0] as typeof json1).others.roles).to.be.deep.equal(json1.others.roles)
expect((json.my_student[0] as typeof json1).jobs).to.be.deep.equal(json1.jobs)
expect((json.my_student[1] as typeof json1).profile.full_name).to.be.eq('noname')
expect((json.my_student[1] as typeof json1).profile.age).to.be.eq(-1)
expect((json.my_student[1] as typeof json1).others.is_male).to.be.eq(false)
expect((json.my_student[1] as typeof json1).others.roles).to.be.deep.equal(['100'])
expect((json.my_student[1] as typeof json1).jobs).to.be.deep.equal({ 2023: ['2023'] })
expect((json.my_student[2] as typeof json1).profile.full_name).to.be.eq('noname')
expect((json.my_student[2] as typeof json1).profile.age).to.be.eq(-1)
expect((json.my_student[2] as typeof json1).others.is_male).to.be.eq(false)
expect((json.my_student[2] as typeof json1).others.roles).to.be.deep.equal(['100'])
expect((json.my_student[2] as typeof json1).jobs).to.be.deep.equal({ 2023: ['2023'] })
expect(json.class_monitor.profile.full_name).to.be.eq(json1.profile.full_name)
expect(json.class_monitor.profile.age).to.be.eq(json1.profile.age)
expect(json.class_monitor.others.is_male).to.be.eq(json1.others.is_male)
expect(json.class_monitor.others.roles).to.be.deep.equal(json1.others.roles)
expect(json.class_monitor.jobs).to.be.deep.equal(json1.jobs)
expect((json.map_student['json1'] as typeof json1).profile.full_name).to.be.eq(json1.profile.full_name)
expect((json.map_student['json1'] as typeof json1).profile.age).to.be.eq(json1.profile.age)
expect((json.map_student['json1'] as typeof json1).others.is_male).to.be.eq(json1.others.is_male)
expect((json.map_student['json1'] as typeof json1).others.roles).to.be.deep.equal(json1.others.roles)
expect((json.map_student['json1'] as typeof json1).jobs).to.be.deep.equal(json1.jobs)
expect((json.map_student['json2'] as unknown as typeof json1).profile.full_name).to.be.eq('noname')
expect((json.map_student['json2'] as unknown as typeof json1).profile.age).to.be.eq(-1)
expect((json.map_student['json2'] as unknown as typeof json1).others.is_male).to.be.eq(false)
expect((json.map_student['json2'] as unknown as typeof json1).others.roles).to.be.deep.equal(['100'])
expect((json.map_student['json2'] as unknown as typeof json1).jobs).to.be.deep.equal({ 2023: ['2023'] })
expect((json.map_student['json3'] as unknown as typeof json1).profile.full_name).to.be.eq('noname')
expect((json.map_student['json3'] as unknown as typeof json1).profile.age).to.be.eq(-1)
expect((json.map_student['json3'] as unknown as typeof json1).others.is_male).to.be.eq(false)
expect((json.map_student['json3'] as unknown as typeof json1).others.roles).to.be.deep.equal(['100'])
expect((json.map_student['json3'] as unknown as typeof json1).jobs).to.be.deep.equal({ 2023: ['2023'] })
})
it('toJson must equal clone.toJson', () => {
const r = new Student(json1)
const json = r.toJson() as typeof json1
const jsonClone = r.clone().toJson() as typeof json1
expect(json).to.be.deep.equal(jsonClone)
const r2 = new ClassRoom()
r2.fromJson<ClassRoom>(json4)
const json2 = r2.toJson() as typeof json4
const json2Clone = r2.clone().toJson() as typeof json4
expect(json2).to.be.deep.equal(json2Clone)
})
})
describe('jsonIgnore test', () => {
it('must be ignore json when call toJson func', () => {
const r = new StudentIgnoreJob(json1)
const json = r.toJson() as typeof json1
expect(r.full_name).to.be.eq(json1.profile.full_name)
expect(r.age).to.be.eq(json1.profile.age)
expect(r.is_male).to.be.eq(json1.others.is_male)
expect(r.roles).to.be.deep.equal(json1.others.roles)
expect(r.jobs).to.be.deep.equal(json1.jobs)
expect(json.profile.full_name).to.be.eq(json1.profile.full_name)
expect(json.profile.age).to.be.eq(json1.profile.age)
expect(json.others.is_male).to.be.eq(json1.others.is_male)
expect(json.others.roles).to.be.deep.equal(json1.others.roles)
expect(json.jobs).to.be.deep.equal(undefined)
})
})
describe('Throw Error when Invalid Schema', () => {
it('must be throw an error when using invalid schema', () => {
expect(() => {
new InvalidStudent()
}).to.be.throw(TypeError, 'Invalid schema: InvalidStudent')
})
})
describe('Common dataType and strict mode', () => {
it('must be parse data to match dataType with no-strict rule', () => {
const n = new NoStrictClass()
n.fromJson(noStrictJson, false)
expect(typeof n.number === 'number')
expect(typeof n.string === 'string')
expect(typeof n.boolean === 'boolean')
expect(Array.isArray(n.array))
expect(typeof n.object === 'object')
expect(n.number).be.eq(Number(noStrictJson.number))
expect(n.string).be.eq(String(noStrictJson.string))
expect(n.boolean).be.eq(Boolean(noStrictJson.boolean))
expect(n.array).be.deep.eq(Array(noStrictJson.array))
expect(n.object).be.deep.eq(Object(noStrictJson.object))
})
it('must be parse to default value with strict rule', () => {
const n = new NoStrictClass()
n.fromJson(noStrictJson)
expect(typeof n.number === 'number')
expect(typeof n.string === 'string')
expect(typeof n.boolean === 'boolean')
expect(Array.isArray(n.array))
expect(typeof n.object === 'object')
expect(n.number).be.eq(0)
expect(n.string).be.eq('')
expect(n.boolean).be.eq(false)
expect(n.array).be.deep.eq([])
expect(n.object).be.deep.eq({})
})
})
describe('Empty DataPath', () => {
it('ArrayNumber', () => {
const r = new ArrayNumber();
expect(r.numbers).to.be.deep.equal([])
const json = [1,2,3]
r.fromJson(json)
expect(r.numbers).to.be.deep.equal(json)
const json2 = [3,5,7]
const r2 = new ArrayNumber(json2);
expect(r2.numbers).to.be.deep.equal(json2)
})
})
const compositeJson = {
name: 'parent',
children: [{
name: 'child_1'
},{
name: 'child_2'
},{
name: 'child_3',
children: [{
name: 'child_3_1',
},{
name: 'child_3_2'
}]
}]
}
class CompositeItem extends Deneric {
name: string = ''
children!: CompositeItem[]
constructor(json?: object) {
super({
name: ['name', String],
children: ['children', Deneric.Array(CompositeItem)]
})
this.fromJson(json)
}
}
describe('Composite', () => {
it('must be success when parse composite item', () => {
const r = new CompositeItem(compositeJson)
console.log('JSON', compositeJson)
console.log('ENTITY', r)
console.log('TO_JSON', r.toJson())
console.log('TO_JSON_stringify', JSON.stringify(r.toJson()))
})
})