UNPKG

@rxap/xml-parser

Version:

Provides a set of decorators and services for parsing and serializing XML documents into TypeScript classes. It simplifies the process of mapping XML elements and attributes to class properties, handling data validation, and serializing objects back into

163 lines 8.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ElementChildrenSerializer = exports.ElementChildrenParser = void 0; exports.ElementChildren = ElementChildren; const tslib_1 = require("tslib"); const mixin_1 = require("@rxap/mixin"); const reflect_metadata_1 = require("@rxap/reflect-metadata"); const utilities_1 = require("@rxap/utilities"); const error_1 = require("../error"); const metadata_keys_1 = require("./metadata-keys"); const child_element_mixin_1 = require("./mixins/child-element.mixin"); const children_element_mixin_1 = require("./mixins/children-element.mixin"); const required_property_1 = require("./required-property"); const utilities_2 = require("./utilities"); let ElementChildrenParser = class ElementChildrenParser { constructor(propertyKey, elementTypeOrFunction, options) { this.propertyKey = propertyKey; this.elementTypeOrFunction = elementTypeOrFunction; this.options = options; this.parse = this.parse.bind(this); Reflect.set(this.parse, 'propertyKey', propertyKey); } parse(xmlParser, element, parsedElement) { // TODO : add test. Persevere child element order with extend child type if (!this.elementType) { throw new Error(`The element type is not defined for <${element.name}>`); } const elementTypes = [this.elementType, ...this.getExtendedTypes(this.elementType)]; const rxapElementChildren = this.getChildren(element); if (!rxapElementChildren) { throw new error_1.RxapXmlParserValidateRequiredError(`The child group element '${this.options.group}' is required for ${parsedElement.__tag}!`, parsedElement.__tag); } const elementChildren = rxapElementChildren .map(childElement => this.attachType(childElement, elementTypes)) .filter(ewt => ewt.type !== null); const children = elementChildren .map(child => { var _a; return xmlParser.parse(child.element, (_a = child.type) !== null && _a !== void 0 ? _a : child.element.name, parsedElement); }); if (this.required && children.length === 0) { throw new error_1.RxapXmlParserValidateRequiredError(`Some element child <${this.tag}> is required in <${parsedElement.__tag}>!`, parsedElement.__tag); } if (this.min !== null && this.min > children.length) { throw new error_1.RxapXmlParserValidateError(`Element child <${this.tag}> should be at least ${this.min} in <${parsedElement.__tag}>!`, parsedElement.__tag); } if (this.max !== null && this.max > children.length) { throw new error_1.RxapXmlParserValidateError(`Element child <${this.tag}> should be at most ${this.max} in <${parsedElement.__tag}>!`, parsedElement.__tag); } if (!(0, utilities_1.hasIndexSignature)(parsedElement)) { throw new Error('The parsed element has no index signature'); } if (!Array.isArray(parsedElement[this.propertyKey])) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore parsedElement[this.propertyKey] = []; } parsedElement[this.propertyKey] .push(...children); return parsedElement; } attachType(element, elementTypes) { var _a, _b; if (this.hasTag) { if (element.hasName(this.tag)) { return { element, type: this.elementType, }; } } else { if (((_a = this.elementType) === null || _a === void 0 ? void 0 : _a.TAG) && element.hasName((_b = this.elementType) === null || _b === void 0 ? void 0 : _b.TAG)) { return { element, type: this.elementType, }; } } const extendedElementType = elementTypes.find(et => et.TAG && element.hasName(et.TAG)); if (extendedElementType) { return { element, type: extendedElementType, }; } return { element, type: null, }; } getExtendedTypes(type) { var _a; const extendedTypes = (_a = (0, reflect_metadata_1.getOwnMetadata)(metadata_keys_1.ElementParserMetaData.EXTENDS, type)) !== null && _a !== void 0 ? _a : []; for (const extendedType of [...extendedTypes]) { extendedTypes.push(...this.getExtendedTypes(extendedType)); } return extendedTypes; } }; exports.ElementChildrenParser = ElementChildrenParser; exports.ElementChildrenParser = ElementChildrenParser = tslib_1.__decorate([ (0, mixin_1.Mixin)(child_element_mixin_1.ChildElementMixin, children_element_mixin_1.ChildrenElementMixin), tslib_1.__metadata("design:paramtypes", [String, Object, Object]) ], ElementChildrenParser); let ElementChildrenSerializer = class ElementChildrenSerializer { constructor(propertyKey, elementTypeOrFunction, options) { this.propertyKey = propertyKey; this.elementTypeOrFunction = elementTypeOrFunction; this.options = options; this.serialize = this.serialize.bind(this); Reflect.set(this.serialize, 'propertyKey', propertyKey); } serialize(xmlParser, element, parsedElement) { // @ts-expect-error the propertyKey is set by the property decorator const children = parsedElement[this.propertyKey]; if (children) { if (!Array.isArray(children)) { throw new error_1.RxapXmlSerializerValidateError(`The property ${this.propertyKey} is not an array!`, parsedElement.__tag); } this.setChildren(element, children, xmlParser); } else if (this.required) { throw new error_1.RxapXmlSerializerValidateRequiredError(`Some element child <${this.tag}> is required in <${parsedElement.__tag}>!`, parsedElement.__tag); } } }; exports.ElementChildrenSerializer = ElementChildrenSerializer; exports.ElementChildrenSerializer = ElementChildrenSerializer = tslib_1.__decorate([ (0, mixin_1.Mixin)(child_element_mixin_1.ChildElementMixin, children_element_mixin_1.ChildrenElementMixin), tslib_1.__metadata("design:paramtypes", [String, Object, Object]) ], ElementChildrenSerializer); /** * Decorator factory that creates a decorator to parse children elements of a specified type from a parent element. * This decorator can be applied to properties within a class to automatically handle the parsing of child elements * based on the specified element type and options. * * @param elementTyp - The type of the child elements to parse. If null, it will parse children without type checking. * @param options - Configuration options for parsing the children elements. Default is an empty object. * Options can include custom parsing rules like filtering or transformations. * * @returns A decorator function that can be applied to a property within a class. This decorator will configure * the property to automatically parse and assign children elements of the specified type. * * Example Usage: * ``` * @ElementChildren(SomeChildElement, { required: true }) * public children: SomeChildElement[]; * ``` * * The decorator modifies the target class's metadata to include a parser for the specified property. If the `required` * option is set to true, it also ensures that the property is marked as required. */ function ElementChildren(elementTyp = null, options = {}) { return function (target, propertyKey) { options = (0, utilities_1.deepMerge)(options, (0, reflect_metadata_1.getMetadata)(metadata_keys_1.ElementParserMetaData.OPTIONS, target, propertyKey) || {}); const parser = new ElementChildrenParser(propertyKey, elementTyp, options); (0, utilities_2.AddParserToMetadata)(parser, target); const serializer = new ElementChildrenSerializer(propertyKey, elementTyp, options); (0, utilities_2.AddSerializerToMetadata)(serializer, target); if (options.required) { (0, required_property_1.RequiredProperty)()(target, propertyKey); } }; } //# sourceMappingURL=element-children.js.map