Я создал простое решение, основанное на вашем описании проблемы. Это может быть не совсем оптимально, но это делает работу (надеюсь). Посмотрите, нужно ли это вам.
Мы будем использовать xml -parse для обработки XML.
TL; DR: Получите полный код здесь .
Итак, чтобы решить эту проблему, мы будем go выполнить два шага.
ШАГ 1: Создать карты XML файлов
Давайте определим структуру данных под названием «карта» (должна была выбрать более описательное имя, но не могла придумать одно). Эта карта будет словарь .
Наша карта состоит из пар ключ-значение.
- Ключ - это путь. Наша карта будет содержать все существующие пути в структуре XML.
- Значение - это другой словарь:
- Ключ - это имя элемента.
- Значение тег элемента.
Таким образом, карты двух приведенных вами примеров XML структур будут выглядеть так:
Старая карта:
{
"/rootDir":{
"childDir":"dir",
"linux.txt":"file",
"img.png":"file"
},
"/rootDir/childDir":{
"hello.jpg":"file"
}
}
Новая карта:
{
"/rootDir":{
"childDir":"dir",
"photos":"dir",
"img.png":"file"
},
"/rootDir/childDir":{
"hello.jpg":"file",
"interesting.brain":"file"
},
"/rootDir/photos":{
"me.dng":"file"
}
}
Рекурсивная функция для построения карты из структуры XML будет выглядеть следующим образом:
// recursive function to build map
function buildMap(element, path, map) {
map[path] = {}
// const childElements = element.childNodes.filter(childNode => childNode.type === 'element');
for (const childNode of element.childNodes) {
// skip text (because the xml-parse package also returns the unnecessary texts in an XML structure, e.g. line breaks)
if (childNode.type === 'text') continue;
// process child element
// add child element's name to indicate that this path has a child with this name
// use child element's type (dir/file) as the value
map[path][childNode.attributes.name] = childNode.tagName;
// if child element is dir, process it recursively
if (childNode.tagName === 'dir') buildMap(childNode, `${path}/${childNode.attributes.name}`, map);
}
}
STEP 2: Получите различия между двумя картами
Теперь мы выведем изменения из карт.
По сути, мы сделаем обход по путям старого map, получите набор дочерних элементов в каждом пути (из обеих карт) и сравните два набора дочерних элементов, чтобы получить необходимые нам изменения.
Функции для этого шага следующие:
// function to get the differences between two maps
function diffMaps(oldMap, newMap) {
const changes = [];
// traverse each path of the old map
for (const key of Object.keys(oldMap)) {
// get children in this path for both old map and new map
const oldChildren = oldMap[key];
const newChildren = newMap[key];
changes.push(...diffChildren(key, oldChildren, newChildren));
}
return changes;
}
// function to get the differences between the children of two maps
function diffChildren(path, oldChildren, newChildren) {
const changes = [];
// traverse each child of the old children
for (const key of Object.keys(oldChildren)) {
// if new children also have that child ==> no change ==> remove that child from new children and continue
if (newChildren[key]) {
// the reason for deleting is that after we have deleted all the keys that are present in old children, the remaining keys in new children will be the newly added ones.
delete newChildren[key];
continue;
}
// new children don't have that child ==> deleted ==> add to changes
const type = oldChildren[key];
changes.push({
node: type === 'dir' ? `<dir name="${key}">` : `<file name="${key}"/>`,
path: path,
change: 'deleted'
});
}
// traverse each child of the new children and add them to changes
for (const key of Object.keys(newChildren)) {
const type = newChildren[key];
changes.push({
node: type === 'dir' ? `<dir name="${key}">` : `<file name="${key}"/>`,
path: path,
change: 'added'
});
}
return changes;
}
ЗАКЛЮЧИТЕЛЬНО: Тестирование
Теперь, когда у нас есть необходимые функции, просто подключите наши данные и запустите:)
const oldXmlString = String.raw`
<dir name="rootDir">
<dir name="childDir">
<file name="hello.jpg"/>
</dir>
<file name="linux.txt"/>
<file name="img.png"/>
</dir>
`.trim();
const newXmlString = String.raw`
<dir name="rootDir">
<dir name="childDir">
<file name="hello.jpg"/>
<file name="interesting.brain"/>
</dir>
<dir name="photos">
<file name="me.dng"/>
</dir>
<file name="img.png"/>
</dir>
`.trim();
const oldXml = xml.parse(oldXmlString);
const newXml = xml.parse(newXmlString);
const oldRoot = oldXml[0];
const newRoot = newXml[0];
// maps with path as key and child nodes' names as value
const oldMap = {};
const newMap = {};
buildMap(oldRoot, `/${oldRoot.attributes.name}`, oldMap);
buildMap(newRoot, `/${newRoot.attributes.name}`, newMap);
const diffs = diffMaps(oldMap, newMap);
console.log(diffs);
Выход:
[ { node: '<file name="linux.txt"/>',
path: '/rootDir',
change: 'deleted' },
{ node: '<dir name="photos">',
path: '/rootDir',
change: 'added' },
{ node: '<file name="interesting.brain"/>',
path: '/rootDir/childDir',
change: 'added' } ]