source: node_modules/minim/lib/serialisers/JSON06Serialiser.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 11.3 KB
Line 
1const JSONSerialiser = require('./JSONSerialiser');
2
3module.exports = class JSON06Serialiser extends JSONSerialiser {
4 serialise(element) {
5 if (!(element instanceof this.namespace.elements.Element)) {
6 throw new TypeError(`Given element \`${element}\` is not an Element instance`);
7 }
8
9 let variable;
10 if (element._attributes && element.attributes.get('variable')) {
11 variable = element.attributes.get('variable');
12 }
13
14 const payload = {
15 element: element.element,
16 };
17
18 if (element._meta && element._meta.length > 0) {
19 payload.meta = this.serialiseObject(element.meta);
20 }
21
22 const isEnum = (element.element === 'enum' || element.attributes.keys().indexOf('enumerations') !== -1);
23
24 if (isEnum) {
25 const attributes = this.enumSerialiseAttributes(element);
26
27 if (attributes) {
28 payload.attributes = attributes;
29 }
30 } else if (element._attributes && element._attributes.length > 0) {
31 let { attributes } = element;
32
33 // Meta attribute was renamed to metadata
34 if (attributes.get('metadata')) {
35 attributes = attributes.clone();
36 attributes.set('meta', attributes.get('metadata'));
37 attributes.remove('metadata');
38 }
39
40 if (element.element === 'member' && variable) {
41 attributes = attributes.clone();
42 attributes.remove('variable');
43 }
44
45 if (attributes.length > 0) {
46 payload.attributes = this.serialiseObject(attributes);
47 }
48 }
49
50 if (isEnum) {
51 payload.content = this.enumSerialiseContent(element, payload);
52 } else if (this[`${element.element}SerialiseContent`]) {
53 payload.content = this[`${element.element}SerialiseContent`](element, payload);
54 } else if (element.content !== undefined) {
55 let content;
56
57 if (variable && element.content.key) {
58 content = element.content.clone();
59 content.key.attributes.set('variable', variable);
60 content = this.serialiseContent(content);
61 } else {
62 content = this.serialiseContent(element.content);
63 }
64
65 if (this.shouldSerialiseContent(element, content)) {
66 payload.content = content;
67 }
68 } else if (this.shouldSerialiseContent(element, element.content) && element instanceof this.namespace.elements.Array) {
69 payload.content = [];
70 }
71
72 return payload;
73 }
74
75 shouldSerialiseContent(element, content) {
76 if (element.element === 'parseResult' || element.element === 'httpRequest'
77 || element.element === 'httpResponse' || element.element === 'category'
78 || element.element === 'link') {
79 return true;
80 }
81
82 if (content === undefined) {
83 return false;
84 }
85
86 if (Array.isArray(content) && content.length === 0) {
87 return false;
88 }
89
90 return true;
91 }
92
93 refSerialiseContent(element, payload) {
94 delete payload.attributes;
95
96 return {
97 href: element.toValue(),
98 path: element.path.toValue(),
99 };
100 }
101
102 sourceMapSerialiseContent(element) {
103 return element.toValue();
104 }
105
106 dataStructureSerialiseContent(element) {
107 return [this.serialiseContent(element.content)];
108 }
109
110 enumSerialiseAttributes(element) {
111 const attributes = element.attributes.clone();
112
113 // Enumerations attribute was is placed inside content (see `enumSerialiseContent` below)
114 const enumerations = attributes.remove('enumerations') || new this.namespace.elements.Array([]);
115
116 // Remove fixed type attribute from samples and default
117 const defaultValue = attributes.get('default');
118 let samples = attributes.get('samples') || new this.namespace.elements.Array([]);
119
120 if (defaultValue && defaultValue.content) {
121 if (defaultValue.content.attributes) {
122 defaultValue.content.attributes.remove('typeAttributes');
123 }
124 // Wrap default in array (not sure it is really needed because tests pass without this line)
125 attributes.set('default', new this.namespace.elements.Array([defaultValue.content]));
126 }
127
128 // Strip typeAttributes from samples, 0.6 doesn't usually contain them in samples
129 samples.forEach((sample) => {
130 if (sample.content && sample.content.element) {
131 sample.content.attributes.remove('typeAttributes');
132 }
133 });
134
135 // Content -> Samples
136 if (element.content && enumerations.length !== 0) {
137 // If we don't have enumerations, content should stay in
138 // content (enumerations) as per Drafter 3 behaviour.
139 samples.unshift(element.content);
140 }
141
142 samples = samples.map((sample) => {
143 if (sample instanceof this.namespace.elements.Array) {
144 return [sample];
145 }
146
147 return new this.namespace.elements.Array([sample.content]);
148 });
149
150 if (samples.length) {
151 attributes.set('samples', samples);
152 }
153
154 if (attributes.length > 0) {
155 return this.serialiseObject(attributes);
156 }
157
158 return undefined;
159 }
160
161 enumSerialiseContent(element) {
162 // In API Elements < 1.0, the content is the enumerations
163 // If we don't have an enumerations, use the value (Drafter 3 behaviour)
164
165 if (element._attributes) {
166 const enumerations = element.attributes.get('enumerations');
167
168 if (enumerations && enumerations.length > 0) {
169 return enumerations.content.map((enumeration) => {
170 const e = enumeration.clone();
171 e.attributes.remove('typeAttributes');
172 return this.serialise(e);
173 });
174 }
175 }
176
177 if (element.content) {
178 const value = element.content.clone();
179 value.attributes.remove('typeAttributes');
180 return [this.serialise(value)];
181 }
182
183 return [];
184 }
185
186 deserialise(value) {
187 if (typeof value === 'string') {
188 return new this.namespace.elements.String(value);
189 }
190
191 if (typeof value === 'number') {
192 return new this.namespace.elements.Number(value);
193 }
194
195 if (typeof value === 'boolean') {
196 return new this.namespace.elements.Boolean(value);
197 }
198
199 if (value === null) {
200 return new this.namespace.elements.Null();
201 }
202
203 if (Array.isArray(value)) {
204 return new this.namespace.elements.Array(value.map(this.deserialise, this));
205 }
206
207 const ElementClass = this.namespace.getElementClass(value.element);
208 const element = new ElementClass();
209
210 if (element.element !== value.element) {
211 element.element = value.element;
212 }
213
214 if (value.meta) {
215 this.deserialiseObject(value.meta, element.meta);
216 }
217
218 if (value.attributes) {
219 this.deserialiseObject(value.attributes, element.attributes);
220 }
221
222 const content = this.deserialiseContent(value.content);
223 if (content !== undefined || element.content === null) {
224 element.content = content;
225 }
226
227 if (element.element === 'enum') {
228 // Grab enumerations from content
229 if (element.content) {
230 element.attributes.set('enumerations', element.content);
231 }
232
233 // Unwrap the sample value (inside double array)
234 let samples = element.attributes.get('samples');
235 element.attributes.remove('samples');
236
237 if (samples) {
238 // Re-wrap samples from array of array to array of enum's
239
240 const existingSamples = samples;
241
242 samples = new this.namespace.elements.Array();
243 existingSamples.forEach((existingSample) => {
244 existingSample.forEach((sample) => {
245 const enumElement = new ElementClass(sample);
246 enumElement.element = element.element;
247 samples.push(enumElement);
248 });
249 });
250
251 const sample = samples.shift();
252
253 if (sample) {
254 element.content = sample.content;
255 } else {
256 element.content = undefined;
257 }
258
259 element.attributes.set('samples', samples);
260 } else {
261 element.content = undefined;
262 }
263
264 // Unwrap the default value
265 let defaultValue = element.attributes.get('default');
266 if (defaultValue && defaultValue.length > 0) {
267 defaultValue = defaultValue.get(0);
268 const defaultElement = new ElementClass(defaultValue);
269 defaultElement.element = element.element;
270 element.attributes.set('default', defaultElement);
271 }
272 } else if (element.element === 'dataStructure' && Array.isArray(element.content)) {
273 [element.content] = element.content;
274 } else if (element.element === 'category') {
275 // "meta" attribute has been renamed to metadata
276 const metadata = element.attributes.get('meta');
277
278 if (metadata) {
279 element.attributes.set('metadata', metadata);
280 element.attributes.remove('meta');
281 }
282 } else if (element.element === 'member' && element.key && element.key._attributes && element.key._attributes.getValue('variable')) {
283 element.attributes.set('variable', element.key.attributes.get('variable'));
284 element.key.attributes.remove('variable');
285 }
286
287 return element;
288 }
289
290 // Private API
291
292 serialiseContent(content) {
293 if (content instanceof this.namespace.elements.Element) {
294 return this.serialise(content);
295 }
296
297 if (content instanceof this.namespace.KeyValuePair) {
298 const pair = {
299 key: this.serialise(content.key),
300 };
301
302 if (content.value) {
303 pair.value = this.serialise(content.value);
304 }
305
306 return pair;
307 }
308
309 if (content && content.map) {
310 return content.map(this.serialise, this);
311 }
312
313 return content;
314 }
315
316 deserialiseContent(content) {
317 if (content) {
318 if (content.element) {
319 return this.deserialise(content);
320 }
321
322 if (content.key) {
323 const pair = new this.namespace.KeyValuePair(this.deserialise(content.key));
324
325 if (content.value) {
326 pair.value = this.deserialise(content.value);
327 }
328
329 return pair;
330 }
331
332 if (content.map) {
333 return content.map(this.deserialise, this);
334 }
335 }
336
337 return content;
338 }
339
340 shouldRefract(element) {
341 if ((element._attributes && element.attributes.keys().length) || (element._meta && element.meta.keys().length)) {
342 return true;
343 }
344
345 if (element.element === 'enum') {
346 // enum elements are treated like primitives (array)
347 return false;
348 }
349
350 if (element.element !== element.primitive() || element.element === 'member') {
351 return true;
352 }
353
354 return false;
355 }
356
357 convertKeyToRefract(key, item) {
358 if (this.shouldRefract(item)) {
359 return this.serialise(item);
360 }
361
362 if (item.element === 'enum') {
363 return this.serialiseEnum(item);
364 }
365
366 if (item.element === 'array') {
367 return item.map((subItem) => {
368 if (this.shouldRefract(subItem) || key === 'default') {
369 return this.serialise(subItem);
370 }
371
372 if (subItem.element === 'array' || subItem.element === 'object' || subItem.element === 'enum') {
373 // items for array or enum inside array are always serialised
374 return subItem.children.map(subSubItem => this.serialise(subSubItem));
375 }
376
377 return subItem.toValue();
378 });
379 }
380
381 if (item.element === 'object') {
382 return (item.content || []).map(this.serialise, this);
383 }
384
385 return item.toValue();
386 }
387
388 serialiseEnum(element) {
389 return element.children.map(item => this.serialise(item));
390 }
391
392 serialiseObject(obj) {
393 const result = {};
394
395 obj.forEach((value, key) => {
396 if (value) {
397 const keyValue = key.toValue();
398 result[keyValue] = this.convertKeyToRefract(keyValue, value);
399 }
400 });
401
402 return result;
403 }
404
405 deserialiseObject(from, to) {
406 Object.keys(from).forEach((key) => {
407 to.set(key, this.deserialise(from[key]));
408 });
409 }
410};
Note: See TracBrowser for help on using the repository browser.