- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
所以我试图找出一种方法来获取两个 XML 树之间的差异(下面的示例),但无法想出任何办法。我需要结果是一个差异数组,数组中的每个元素都包含已更改的节点、更改方式(添加、删除)以及节点的路径。
编辑:忘了提及,XML 的顺序并不重要。我尝试使用 npm/dom-compare,但它并没有完全给出所需的结果(使用下面的示例),因为它不希望看到新标签(目录照片),但没有提供有关它发现的任何信息意外的标签。
1.
<dir name="rootDir">
<dir name="childDir">
<file name="hello.jpg"/>
</dir>
<file name="linux.txt"/>
<file name="img.png"/>
</dir>
2.
<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>
我的 XML 源将仅包含 和 标签。
例如,在上面的两个 XML 文档中,compare(1, 2) 应该导致:(就我的目的而言,没有“更改”的更改,例如,如果文件名发生更改,则它是一个新文件,而旧文件则为旧文件)其中一个被视为已删除而不是移动,并且如果其文件发生更改,则不包括目录)。
[
{node: '<file name="interesting.brain"/>', path: '/rootDir/childDir' change: 'added'},
{node: '<dir name="photos">', path: '/rootDir', change: 'added'}
{node: '<file name="linux.txt"/>', path: '/rootDir', change: 'deleted'}
]
我的第一个想法是首先使用 fast-xml-parser 将 XML 字符串解析为 JS 对象,这会产生以下对象:
1.
{ dir: [
{
name: 'rootDir',
dir: [
{
name: 'childDir',
file: [
{ name: 'hello.jpg' }
]
}
],
file: [
{ name: 'linux.txt' },
{ name: 'img.png' }
]
}
] }
2.
{ dir: [
{
name: 'rootDir',
dir: [
{
name: 'childDir',
file: [
{ name: 'hello.jpg' },
{ name: 'interesting.brain' }
]
},
{
name: 'photos',
file: [
{ name: 'me.dng' }
]
}
],
file: [
{ name: 'img.png' }
]
}
] }
然而,这会导致额外的复杂性,因为生成的格式使用数组和对象,这至少增加了弄清楚如何区分两者的心理工作量。它也可能会慢一些,因为显然您必须首先解析 XML 字符串,更不用说添加第 3 方库了。
寻找可以用来解决此问题的任何建议或伪代码算法。应该注意我正在使用 Typescript 并针对 ES6/Node.js。
干杯。
最佳答案
我根据您对问题的描述创建了一个简单的解决方案。它可能不是真正最佳的,但它完成了工作(希望如此)。看看这是否是您所需要的。
我们将使用 xml-parse处理 XML 的包。
TL;DR: 获取完整代码 here .
因此,为了解决这个问题,我们将分两步进行。
第 1 步:创建 XML 文件的映射
让我们定义一个名为“map”的数据结构(应该选择一个更具描述性的名称,但想不出一个)。这张 map 将是 dictionary .
我们的 map 由键值对组成。
因此,您提供的两个示例 XML 结构的映射将如下所示:
旧 map :
{
"/rootDir":{
"childDir":"dir",
"linux.txt":"file",
"img.png":"file"
},
"/rootDir/childDir":{
"hello.jpg":"file"
}
}
新 map :
{
"/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);
}
}
第 2 步:获取两张 map 之间的差异
现在我们将从 map 中得出更改。
基本上,我们要做的就是遍历旧 map 的路径,获取每个路径中的子集(从两个 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' } ]
关于javascript - 获取两个 XML 树之间差异的算法(JS 或伪代码),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60481648/
关于 B 树与 B+ 树,网上有一个比较经典的问题:为什么 MongoDb 使用 B 树,而 MySQL 索引使用 B+ 树? 但实际上 MongoDb 真的用的是 B 树吗?
如何将 R* Tree 实现为持久(基于磁盘)树?保存 R* 树索引或保存叶值的文件的体系结构是什么? 注意:此外,如何在这种持久性 R* 树中执行插入、更新和删除操作? 注意事项二:我已经实现了一个
目前,我正在努力用 Java 表示我用 SML 编写的 AST 树,这样我就可以随时用 Java 遍历它。 我想知道是否应该在 Java 中创建一个 Node 类,其中包含我想要表示的数据,以及一个数
我之前用过这个库http://www.cs.umd.edu/~mount/ANN/ .但是,它们不提供范围查询实现。我猜是否有一个 C++ 范围查询实现(圆形或矩形),用于查询二维数据。 谢谢。 最佳
在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择
书接上回,今天和大家一起动手来自己实现树。 相信通过前面的章节学习,大家已经明白树是什么了,今天我们主要针对二叉树,分别使用顺序存储和链式存储来实现树。 01、数组实现 我们在上一节中说过,
书节上回,我们接着聊二叉树,N叉树,以及树的存储。 01、满二叉树 如果一个二叉树,除最后一层节点外,每一层的节点数都达到最大值,即每个节点都有两个子节点,同时所有叶子节点都在最后一层,则这个
树是一种非线性数据结构,是以分支关系定义的层次结构,因此形态上和自然界中的倒挂的树很像,而数据结构中树根向上树叶向下。 什么是树? 01、定义 树是由n(n>=0)个元素节点组成的
操作系统的那棵“树” 今天从一颗 开始,我们看看如何从小树苗长成一颗苍天大树。 运转CPU CPU运转起来很简单,就是不断的从内存取值执行。 CPU没有好好运转 IO是个耗费时间的活,如果CPU在取值
我想为海洋生物学类(class)制作一个简单的系统发育树作为教育示例。我有一个具有分类等级的物种列表: Group <- c("Benthos","Benthos","Benthos","Be
我从这段代码中删除节点时遇到问题,如果我插入数字 12 并尝试删除它,它不会删除它,我尝试调试,似乎当它尝试删除时,它出错了树的。但是,如果我尝试删除它已经插入主节点的节点,它将删除它,或者我插入数字
B+ 树的叶节点链接在一起。将 B+ 树的指针结构视为有向图,它不是循环的。但是忽略指针的方向并将其视为链接在一起的无向叶节点会在图中创建循环。 在 Haskell 中,如何将叶子构造为父内部节点的子
我在 GWT 中使用树控件。我有一个自定义小部件,我将其添加为 TreeItem: Tree testTree = new Tree(); testTree.addItem(myWidget); 我想
它有点像混合树/链表结构。这是我定义结构的方式 struct node { nodeP sibling; nodeP child; nodeP parent; char
我编写了使用队列遍历树的代码,但是下面的出队函数生成错误,head = p->next 是否有问题?我不明白为什么这部分是错误的。 void Levelorder(void) { node *tmp,
例如,我想解析以下数组: var array1 = ["a.b.c.d", "a.e.f.g", "a.h", "a.i.j", "a.b.k"] 进入: var json1 = { "nod
问题 -> 给定一棵二叉树和一个和,确定该树是否具有从根到叶的路径,使得沿路径的所有值相加等于给定的和。 我的解决方案 -> public class Solution { public bo
我有一个创建 java 树的任务,它包含三列:运动名称、运动类别中的运动计数和上次更新。类似的东西显示在下面的图像上: 如您所见,有 4 种运动:水上运动、球类运动、跳伞运动和舞蹈运动。当我展开 sk
我想在 H2 数据库中实现 B+ Tree,但我想知道,B+ Tree 功能在 H2 数据库中可用吗? 最佳答案 H2 已经使用了 B+ 树(PageBtree 类)。 关于mysql - H2数据库
假设我们有 5 个字符串数组: String[] array1 = {"hello", "i", "cat"}; String[] array2 = {"hello", "i", "am"}; Str
我是一名优秀的程序员,十分优秀!