1 |
|
2 | import {IModelMapper, IModel} from "./Types";
|
3 | import {TypeStoreAttrKey, TypeStoreModelKey} from "./Constants";
|
4 | import {IModelAttributeOptions, IModelOptions} from "./decorations/ModelDecorations";
|
5 |
|
6 | const JSONFormattingSpace = (process.env.NODE_ENV !== 'production') ? 4 : 0
|
7 |
|
8 | export class ModelMapper<M extends IModel> implements IModelMapper<M> {
|
9 |
|
10 |
|
11 | private modelAttrs:IModelAttributeOptions[]
|
12 | private modelOpts:IModelOptions
|
13 |
|
14 | constructor(private modelClazz:{new():M}) {
|
15 | this.modelOpts = Reflect.getMetadata(TypeStoreModelKey,this.modelClazz)
|
16 | this.modelAttrs = Reflect.getMetadata(TypeStoreAttrKey,this.modelClazz)
|
17 | }
|
18 |
|
19 | private attr(key:string) {
|
20 | for (let it of this.modelAttrs) {
|
21 | if (it.name === key) {
|
22 | return it
|
23 | }
|
24 | }
|
25 |
|
26 | return null
|
27 | }
|
28 |
|
29 | private attrTransient(key:string) {
|
30 | const attr = this.attr(key)
|
31 | return (
|
32 | this.modelOpts &&
|
33 | this.modelOpts.transientAttrs &&
|
34 | this.modelOpts.transientAttrs.includes(key)
|
35 | ) || (
|
36 | attr && attr.transient === true
|
37 | )
|
38 | }
|
39 |
|
40 | private attrPersists(key:string) {
|
41 | return !this.attrTransient(key) && (this.modelOpts.onlyMapDefinedAttributes !== true || this.attr(key))
|
42 | }
|
43 |
|
44 | toJson(o:M):string {
|
45 | return JSON.stringify(o,(key,value) => {
|
46 | return (this.attrPersists(key)) ? value : undefined
|
47 | },JSONFormattingSpace)
|
48 | }
|
49 |
|
50 | toObject(o:M):Object {
|
51 | const obj = {}
|
52 | const keys = Object.keys(o)
|
53 |
|
54 |
|
55 |
|
56 | for (let modelAttr of this.modelAttrs) {
|
57 | const included = keys.includes(modelAttr.name)
|
58 | if (modelAttr.transient && included)
|
59 | keys.splice(keys.indexOf(modelAttr.name),1)
|
60 | else if (!included)
|
61 | keys.push(modelAttr.name)
|
62 | }
|
63 | for (let key of keys) {
|
64 | if (this.attrPersists(key))
|
65 | obj[key] = o[key]
|
66 | }
|
67 |
|
68 |
|
69 | return obj
|
70 | }
|
71 |
|
72 |
|
73 | fromJson(json:string,decorator = null):M {
|
74 | const jsonObj = JSON.parse(json,(key,value) => {
|
75 | return (!this.attrPersists(key)) ? undefined : value
|
76 | })
|
77 |
|
78 | const o = new this.modelClazz()
|
79 | Object.assign(o,jsonObj)
|
80 | if (decorator)
|
81 | return decorator(jsonObj,o)
|
82 |
|
83 | return o
|
84 | }
|
85 |
|
86 | fromObject(obj:Object,decorator = null):M {
|
87 | const o = new this.modelClazz()
|
88 | for (let key of Object.keys(obj)) {
|
89 | if (this.attrPersists(key))
|
90 | o[key] = obj[key]
|
91 | }
|
92 |
|
93 | if (decorator)
|
94 | return decorator(obj,o)
|
95 |
|
96 | return o
|
97 | }
|
98 | }
|
99 |
|
100 | const mapperCache = new WeakMap<any,IModelMapper<IModel>>()
|
101 |
|
102 | export function getDefaultMapper<T extends IModel>(clazz:{new():T}):IModelMapper<T> {
|
103 | let mapper = mapperCache.get(clazz) as IModelMapper<T>
|
104 |
|
105 | if (!mapper) {
|
106 | mapper = new ModelMapper(clazz)
|
107 | mapperCache.set(clazz,(mapper))
|
108 | }
|
109 |
|
110 | return mapper
|
111 |
|
112 | } |
\ | No newline at end of file |