@open-kappa/myjson
Version:
A simple JSON management library.
231 lines • 7.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MyJsonFlex = void 0;
const myjsonImpl_1 = require("./myjsonImpl");
/** Key for symbol name. */
const KEY_NAME = "_okmj_flex_impl";
/**
* @brief A special kind of JSON object.
* It has some mandatory fields, and other optionals, as long as they respect a
* validator.
* It is inspired by C "flex" structs, i.e. structs with some fields ending with
* an array:
* ```c
* struct FlexExample
* {
* int a;
* double b;
* unsigned flex[0];
* };
* ```
*/
class MyJsonFlex extends myjsonImpl_1.MyJsonBaseObject {
/**
* @brief Constructor.
* @param {Element} element The validator for flex elements. If it is
* mandatory, then the list cannot be empty.
* @param {boolean} isMandatory True if the object must appear in the
* hierarchy.
* @param {string} name The name of the object.
*/
constructor(element, isMandatory, name) {
super(isMandatory, name);
const anySelf = this;
anySelf[Symbol.for(KEY_NAME)] =
new myjsonImpl_1.MyJsonFlexProperties(element);
}
/**
* @brief Return the internal validator element of the flex object.
* It uses a symbol to hide the properties to "usual" javascript methods.
* @private
* @return {Element} The validator.
*/
_getValidatorElement() {
const anySelf = this;
return anySelf[Symbol.for(KEY_NAME)].element;
}
/**
* @brief Parse a JSON object.
* @param {any} json The JSON to parse.
* @throws {Error} If a validation error occurrs.
*/
parseJsonImpl(json) {
const self = this;
self._ensureMandatory(json);
self._parseJson(json);
}
/**
* @brief Ensure that mandatory properties exists.
* @param {any} json The json to validate.
* @throws {Error} If the check fails.
*/
_ensureMandatory(json) {
const self = this;
function ensureMandatory(key, value) {
if (!value.isMandatory()) {
return false;
}
else if (!(value.getJsonName() in json)) {
self._throwValidatorError("MyJsonFlex.parseJson(): missing mandatory key: " + key);
}
return false;
}
self.forEach(ensureMandatory);
const element = self._getValidatorElement();
if (!element.isMandatory())
return;
if (self.getSize() < Object.keys(json).length)
return;
self._throwValidatorError("MyJsonFlex.parseJson(): expecting at least one optional element.");
}
/**
* @brief Do actual parsing.
* @param {any} json The json to parse.
* @throws {Error} If the parsing fails.
*/
_parseJson(json) {
const self = this;
const keys = Object.keys(json);
const set = new Set();
const element = self._getValidatorElement();
for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
if (set.has(key)) {
self._throwValidatorError("MyJsonFlex.parseJson(): duplicated key " + key);
}
set.add(key);
const jsonValue = json[key];
if (self.has(key)) {
const el = self.get(key);
el.parseJson(jsonValue);
}
else {
const el = element.clone();
el.parseJson(jsonValue);
self.set(key, el);
}
}
}
// @todo move as utility, since here it is unused
_parseRecurse(key, jsonValue) {
const self = this;
const type = typeof jsonValue;
switch (type) {
case "boolean":
{
const newValue = new myjsonImpl_1.MyJsonValue(false, false, key);
newValue.parseJson(jsonValue);
self.set(key, newValue);
break;
}
case "number":
{
const newValue = new myjsonImpl_1.MyJsonValue(0, false, key);
newValue.parseJson(jsonValue);
self.set(key, newValue);
break;
}
case "object":
{
const newValue = new MyJsonFlex(self._getValidatorElement().clone(), false, key);
newValue.parseJson(jsonValue);
self.set(key, newValue);
break;
}
case "string":
{
const newValue = new myjsonImpl_1.MyJsonValue("", false, key);
newValue.parseJson(jsonValue);
self.set(key, newValue);
break;
}
case "bigint":
case "function":
case "symbol":
case "undefined":
default:
self._throwValidatorError("unexpected JSON type: " + type);
}
}
/**
* @brief Clear the object content.
*/
clearImpl() {
const self = this;
const anySelf = self;
function deleter(key, el) {
if (!el.isMandatory()) {
delete anySelf[key];
}
else {
el.clear();
}
return false;
}
self.forEach(deleter);
}
/**
* @brief Clone this object.
* @return {MyJsonFlex<Element>} The copy.
*/
clone() {
const self = this;
const ret = new MyJsonFlex(self._getValidatorElement().clone(), self.isMandatory(), self.getJsonName());
self._cloneImpl(ret);
return ret;
}
/**
* @brief Check if two JSON objects are equals.
* @param {MyJson} other The other object of comparison.
* @return {boolean} True if they are equals.
*/
isEqual(other) {
const self = this;
if (!(other instanceof MyJsonFlex))
return false;
return self._isEqualImpl(other);
}
/**
* @brief Execute the given callback on each element.
* The callback takes the key, the element, and the whole object.
* It returns true to break the loop before having rolled on all elements.
* @param {(key:string,value:MyJson,obj:MyJsonFlex<Element>)=>boolean} func
* The callback.
* @param {any | null | undefined} [funcThis=null] The optional "this" for
* the callback.
*/
forEach(func, funcThis = null) {
return super._forEachImpl(func, funcThis);
}
/**
* @brief Merge the two objects.
* Already existent keys are merged recursively.
* @param {MyJson} other The other object to merge.
* @throws {Error} If the two objects cannot be merged.
*/
merge(other) {
const self = this;
if (!(other instanceof MyJsonFlex)) {
self._throwValidatorError("Invalid other instance.");
}
const otherObj = other;
if (!self._getValidatorElement().isEqual(otherObj._getValidatorElement())) {
self._throwValidatorError("Invalid other type instance.");
}
self._mergeImpl(otherObj);
}
/**
* @brief Add a new child element.
* This is a build-time support method.
* @param {MyJson} element The child element.
* @return {MyJson} This.
* @throws {Error} If element cannot be added.
*/
add(element) {
super.add(element);
return this;
}
}
exports.MyJsonFlex = MyJsonFlex;
exports.default = MyJsonFlex;
//# sourceMappingURL=MyJsonFlex.js.map