178 lines
6.5 KiB
TypeScript
178 lines
6.5 KiB
TypeScript
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces.js';
|
|
import { create } from 'xmlbuilder2';
|
|
import Dataset from '#app/Models/Dataset';
|
|
import Field from './Field.js';
|
|
import BaseModel from '#app/Models/BaseModel';
|
|
import { DateTime } from 'luxon';
|
|
|
|
export default class Strategy {
|
|
private version: number;
|
|
private config;
|
|
private xml: XMLBuilder;
|
|
|
|
constructor(config: any) {
|
|
this.version = 1.0;
|
|
this.config = config;
|
|
}
|
|
public async createDomDocument(): Promise<XMLBuilder> {
|
|
if (this.config.model === null) {
|
|
throw new Error('No Model given for serialization.');
|
|
}
|
|
// domDocument = create({ version: '1.0', encoding: 'UTF-8', standalone: true }, '<root></root>');
|
|
|
|
this.xml = create({ version: '1.0', encoding: 'UTF-8' }, '<Opus></Opus>');
|
|
this.xml.root().att('version', this.version.toString());
|
|
// this.xml.root().att('version', this.getVersion());
|
|
this.xml.root().att('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
|
|
|
await this._mapModel(this.config.model);
|
|
return this.xml; //.end({ prettyPrint: true });
|
|
}
|
|
|
|
private async _mapModel(model: Dataset) {
|
|
const fields: Array<string> = await model.describe();
|
|
const excludeFields = this.getConfig().excludeFields;
|
|
let fieldsDiff;
|
|
|
|
if (excludeFields.length > 0) {
|
|
fieldsDiff = fields.filter((fieldname) => !excludeFields.includes(fieldname));
|
|
} else {
|
|
fieldsDiff = fields;
|
|
}
|
|
|
|
const modelNode: XMLBuilder = this.createModelNode(model);
|
|
// rootNode.appendChild(childNode);
|
|
|
|
for (const fieldname of fieldsDiff) {
|
|
const field = model.getField(fieldname);
|
|
this.mapField(field as Field, modelNode);
|
|
}
|
|
}
|
|
|
|
private mapField(field: Field, modelNode: XMLBuilder) {
|
|
const modelClass = field.getValueModelClass();
|
|
let fieldValues = field.getValue();
|
|
|
|
if (this.config.excludeEmpty) {
|
|
if (
|
|
fieldValues === null ||
|
|
(typeof fieldValues === 'string' && fieldValues.trim() === '') ||
|
|
(Array.isArray(fieldValues) && fieldValues.length === 0)
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (modelClass === null) {
|
|
this.mapSimpleField(modelNode, field);
|
|
} else {
|
|
// map related models with values:
|
|
// let modelInstance = new modelClass();
|
|
const fieldName = field.getName();
|
|
|
|
if (!Array.isArray(fieldValues)) {
|
|
fieldValues = [fieldValues];
|
|
}
|
|
|
|
for (const value of fieldValues) {
|
|
const childNode = modelNode.ele(fieldName);
|
|
// rootNode.appendChild(childNode);
|
|
// if a field has no value then there is nothing more to do
|
|
// TODO maybe there must be another solution
|
|
if (value === null) {
|
|
continue;
|
|
}
|
|
if (modelClass.prototype instanceof BaseModel) {
|
|
this.mapModelAttributes(value, childNode);
|
|
} else if (modelClass instanceof DateTime) {
|
|
// console.log('Value is a luxon date');
|
|
this.mapDateAttributes(value, childNode);
|
|
} else if (Array.isArray(value)) {
|
|
console.log('Value is an array');
|
|
// this.mapArrayAttributes(value, childNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private mapDateAttributes(model: DateTime, childNode: XMLBuilder) {
|
|
childNode.att('Year', model.year.toString());
|
|
childNode.att('Month', model.month.toString());
|
|
childNode.att('Day', model.day.toString());
|
|
childNode.att('Hour', model.hour.toString());
|
|
childNode.att('Minute', model.minute.toString());
|
|
childNode.att('Second', model.second.toString());
|
|
childNode.att('UnixTimestamp', model.toUnixInteger().toString());
|
|
let zoneName = model.zoneName ? model.zoneName : '';
|
|
childNode.att('Timezone', zoneName);
|
|
}
|
|
|
|
private mapModelAttributes(myObject: any, childNode: XMLBuilder) {
|
|
Object.keys(myObject).forEach((prop) => {
|
|
let value = myObject[prop];
|
|
// console.log(`${prop}: ${value}`);
|
|
if (value != null) {
|
|
if (value instanceof DateTime) {
|
|
value = value.toFormat('yyyy-MM-dd HH:mm:ss').trim();
|
|
} else {
|
|
value = value.toString().trim();
|
|
}
|
|
|
|
// Replace invalid XML-1.0-Characters by UTF-8 replacement character.
|
|
let fieldValues = value.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '\xEF\xBF\xBD ');
|
|
|
|
// Create an attribute with the field name and field values
|
|
// const attr = { [fieldName]: fieldValues };
|
|
|
|
// Add the attribute to the root element
|
|
childNode.att(prop, fieldValues);
|
|
}
|
|
});
|
|
}
|
|
|
|
private mapSimpleField(modelNode: XMLBuilder, field: Field) {
|
|
const fieldName = field.getName();
|
|
let fieldValues = this.getFieldValues(field);
|
|
|
|
if (fieldValues != null) {
|
|
// Replace invalid XML-1.0-Characters by UTF-8 replacement character.
|
|
fieldValues = fieldValues.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '\xEF\xBF\xBD ');
|
|
|
|
// Create an attribute with the field name and field values
|
|
// const attr = { [fieldName]: fieldValues };
|
|
|
|
// Add the attribute to the root element
|
|
modelNode.att(fieldName, fieldValues);
|
|
}
|
|
}
|
|
|
|
private getFieldValues(field: any): string {
|
|
let fieldValues: number | string | Array<string> = field.getValue(); //275
|
|
|
|
// workaround for simple fields with multiple values
|
|
if (field.hasMultipleValues() === true && Array.isArray(fieldValues)) {
|
|
fieldValues = fieldValues.join(',');
|
|
}
|
|
// Uncomment if needed
|
|
// if (fieldValues instanceof DateTimeZone) {
|
|
// fieldValues = fieldValues.getName();
|
|
// }
|
|
|
|
return fieldValues?.toString().trim();
|
|
}
|
|
|
|
private createModelNode(model: Dataset) {
|
|
const className = 'Rdr_' + model.constructor.name.split('\\').pop(); //Rdr_Dataset
|
|
// return dom.createElement(className);
|
|
return this.xml.root().ele(className);
|
|
}
|
|
|
|
// private getVersion() {
|
|
// return Math.floor(this.version);
|
|
// }
|
|
|
|
private getConfig() {
|
|
return this.config;
|
|
}
|
|
}
|