DevGang
Авторизоваться

ShortXML aka xml с переменными

Я создал javascript библиотеку - сериализатор/десериализатор, где одинаковые части объекта выводятся только один раз.

Например

input

{
    obj: {
        fizz: {
            lol: 'kek'
        },
        buzz: {
            lol: 'kek'
        },
    },
    obj2: {
        fizz: {
            lol: 'kek'
        },
        buzz: {
            lol: 'kek'
        },
        fizzbuzz: {
            lol: 'kek'
        }
    },
}

Превратится

<root>
    <obj>
        <fizz shortxml_var="14">
            <lol shortxml_var="15">
                kek
            </lol>
        </fizz>
        <buzz shortxml_var="16">
            <lol shortxml_var="15"/>
        </buzz>
    </obj>
    <obj2>
        <fizz shortxml_var="14"/>
        <buzz shortxml_var="16"/>
        <fizzbuzz>
            <lol shortxml_var="15"/>
        </fizzbuzz>
    </obj2>
</root>

Итак, начнем с сериализации:
превращаем наш объект в dom, находим одинаковые узлы и собираем строку.

let i = 0;
//uuid 
let uuidv4 = () => {
    return i++;
}
//step 1 - create tree
let start = new node();
start.key = "root";
let stack = [start];
//создаем дерево
let createTree = (obj, prevKey, prevIsArray = false) => {
    for (let key in obj) {
        let el = new node();
        el.id = uuidv4();
        el.key = (prevIsArray) ? prevKey : key;
        //в эксеемеле массивы идут без родительской ноды
        el.isArray = Array.isArray(obj[key]);
        stack[stack.length - 1].childrens.push(el);
        if ((/boolean|number|string/).test(typeof obj[key])) {
            el.value = obj[key];
        } else {
            stack.push(el);
            createTree(obj[key], key, el.isArray);
            stack.pop();
        }
    }
}
createTree(obj);
//step 2 find clone
//ищем одинаковые ноды
let shorting = (node) => {
    let cNode = (node);
    let variable = uuidv4();
    let setVariable = (node) => {
        if (cNode.id !== node.id) {
            function equal(a, b) {
                if (a?.childrens.length && b?.childrens.length) {
                    for (let p in a.childrens) {
                        if (!equal(a.childrens[p], b.childrens[p])) {
                            return false;
                        }
                    }
                }
                if (a?.value === b?.value) {
                    if (a?.key === b?.key) {
                        return true;
                    }
                }
                return false;
            }
            //если нашли, изменим им свойство вариеблес
            if (equal(node, cNode)) {
                node.variable = variable;
                cNode.variable = variable;
            } else {
                node.childrens.forEach((item) => {
                    setVariable(item);
                });
            }
        }
  
    };
    setVariable(stack[0]);
    node.childrens.forEach((item) => {
        //если нода уже найдена, пропускаем ее
         if (!item.variable) {
             shorting(item);
         }
    })
};
shorting(stack[0]);
  
//step 3 build xml
//собираем строку 
let variable = [];
let resXML = '';
let buildXml = (node) => {
    //если переменная уже распечатана, не печатаем ее
    if (variable[node.variable]) {
        resXML += `<${node.key} shortxml_var="${node.variable}"/>`;
        return;
    }
    //печатаем переменную
    if (node.variable) {
        variable[node.variable] = true;
        if (node.isArray) {
            //выведем связующий узел для массива
            resXML += `<${node.key} shortxml_isarray="true" shortxml_var="${node.variable}">`;
        } else {
            resXML += `<${node.key} shortxml_var="${node.variable}">`;
        }
    } else {
        if (!node.isArray) {
            resXML += "<" + node.key + ">";
        }
    }
    if (node.value && !node.isArray) {
        resXML += node.value;
    }
    node.childrens.forEach((item) => {
        buildXml(item, isVariable);
    });
    if (!node.isArray || node.variable) {
        resXML += "</" + node.key + ">";
    }
}
buildXml(stack[0])
return resXML;

Теперь десериализация:
сперва соберем dom, затем создаем объект — сначала найдем все ноды с переменной, соберем их и сложим в variables, затем обойдем дерево еще раз и соберем наш объект.

//step 1 build dom
//собираем дерево
let startNode = new node();
let stack = [];
stack.push(startNode);
superxmlparser74.parse(str,
    (item) => {
        let el = new node();
        el.tag = item.tag.trim();
        let variable = item.attr.find((item) => item.key === "shortxml_var")?.value[0];
        el.isarray = item.attr.find((item) => item.key === "shortxml_isarray")?.value[0];
 
        el.attr = item.attr.filter((item) => {
            return !item.key.includes("shortxml")
        });
        el.var = variable;
        el.varPrint = (varPrint === 'true') ? true : false;
 
        stack[stack.length - 1].childrens.push(el);
        stack.push(el);
    },
    (item) => {
        stack[stack.length - 1].value = item.value.trim();
    },
    (item) => {
        stack.pop();
    },
    () => {
 
    }, (item) => {
        //selfclosing тут нераспечатанные переменные
        let el = new node();
        el.tag = item.tag.trim();
        let variable = item.attr.find((item) => item.key === "shortxml_var")?.value[0];
        el.attr = item.attr.filter((item) => {
            return !item.key.includes("shortxml")
        });
        el.var = variable;
        el.varPrint = (varPrint === 'true') ? true : false;
 
        stack[stack.length - 1].childrens.push(el);
    });
startNode = startNode.childrens[0];
 
//step 2 build obj
let obj = {};
let variables = [];
//функция для сборки обьектов
let deep = (node, tNode) => {
    let t = {};
    //вытаскиваем готовый обьект
    if (node.var && variables[node.var]) {
        t = JSON.parse(JSON.stringify(variables[node.var]))
    }
    if (node.childrens.length) {
        if (Array.isArray(tNode[node.tag])) {
            tNode[node.tag].push(t);
        } else if (tNode[node.tag]) {
            tNode[node.tag] = [tNode[node.tag]];
            tNode[node.tag].push(t);
        } else {
            tNode[node.tag] = t;
        }
        if (node.var && variables[node.var]) {
            return;
        }
        node.attr.forEach((item) => {
            t[item.key] = item.value[0];
        });
        node.childrens.forEach((item) => {
            deep(item, t, buildVar);
        });
    } else {
        if (node.var && variables[node.var]) {
            node.value = t;
        }
        if (Array.isArray(tNode[node.tag])) {
            tNode[node.tag].push(node.value);
        } else if (tNode[node.tag]) {
            tNode[node.tag] = [tNode[node.tag]];
            tNode[node.tag].push(node.value);
        } else {
            tNode[node.tag] = node.value;
        }
    }
}
//generate var obj;
//функция для сборки всех переменных
let getVar = (node) => {
    if (node.childrens.length) {
        if (node.var && !variables[node.var]) {
            let cObjVar = {}
            if (node.isarray) {
                //массивы
                cObjVar = node.childrens.map((elArrNode) => {
                    let c = {};
                    deep(elArrNode, c, true);
                    return c[elArrNode.tag];
                });
                variables[node.var] = cObjVar;
            } else {
                //обычные ноды
                deep(node, cObjVar, true);
                variables[node.var] = cObjVar[node.tag];
            }
        }
        node.childrens.forEach((item) => {
            getVar(item);
        });
    } else {
        if (node.var && !variables[node.var]) {
            variables[node.var] = node.value;
        }
    }
}
//обойдем дерево и сложим все переменные в variables
getVar(startNode);
//соберем наш объект
deep(startNode, obj);
 
return obj['root'];

Весь код доступен в гитхабе — https://github.com/ru51a4/shortXML
Решение в npmjs — https://www.npmjs.com/package/shortxml

Источник:

#JavaScript #Алгоритмы
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу