判断两个XML文件结构与内容是否相同
1. 引入
目前公司的这款软件导入导出数据库信息的方法是:组织数据的内容和结构 利用MS com的sax解析 储存数据为XML格式
优点是可以选择部分导出
缺点是速度慢文件导出的文件庞大,若客户出现导入导出错误要重现或者调试困难
由于软件持续更新所以不同版本的,XML有差异
现客户需求将高版本的软件导出成 低版本XML,保证从任何高版本导出的文件 与 低版本 原先的格式与内容一致
我们实现了功能之后为了测试功能,要写单元测试
2. 分析
要测试高版本导出的文件的 内容和格式能完全导出成低版本
首先要通过低版本 生成一个数据库,将内容全部导出成A
再将A导入到高版本确保A的信息完全转化成高版本的结构
将高版本导出成低版本文件B
比较AB的内容和结构
B除某些少部分信息与结构,其他必须完全与低版本一致
3. 实现
将A与B通过 dom 解析,取出各自的节点进行一一比较直到所有在A中的节点的位置和信息都与B中一致后,才可判定2者一致,由于导出时数据库的结构不同,XML的文件中节点的顺序可能不同在我们的软件中这种情况是可以认为是一致的。顺序不用考虑,因此要求UT忽略顺序
比较主要分为3步
1)在A和B中取出某个节点
2)比较两个节点的信息是否一致
3)比较这两个节点的子节点信息是否一致
XML格式为树状结构,除根节点和叶子节点外每个节点均有父节点和子节点,另外本软件导出的文件只会有一个根节点
因此可以进行递归查找进一步把流程改为:
1)从A和B中取出根节点,对根节点进行compare(若一致则可以确定两文件内容、结构一致)
2)compare流程
(1)比较当前节点信息若不一致则返回失败
(2)若一致,则取出AB所有的子节点,遍历A的子节点 去 B的子节点集中查找 (递归调用Compare进行判断)若所有A的子节点都能在B中找到,则判断AB节点相同(前提AB中子节点数量一致)
#include "ModelParser.h"
#include "IExporterDocumentTranslator.h"
.....将低版本文件导入到高版本
.....导出成低版本
ModelParser version4Parser(fileOfversion4);//ModelParser类用来解析XML文件
ModelParser exportedFileParser(exportedFile);
ASSERT(version4Parser.IsValid());
ASSERT(exportedFileParser.IsValid());
//Compare it with the 4.0 file, their datas should be consistent
ASSERT(version4Parser.Compare(exportedFileParser));
ModelParser类及实现
ModelParser.h
#pragma once
#include"XMLDOMNodeParser.h"
class ModelParser
{
public:
ModelParser(const String& modelFilePath);
/////////////////////////////////////////////////////////////////////////
// Compare the content with another file, if the files are the same return true (exportas test use only)
bool Compare(const ModelParser& otherModel) const;
bool IsValid() const;
private:
shared_ptr<XMLDOMNodeParser> rootInterchangeFile;
IXMLDOMNodePtr GetInterchangeFile(IXMLDOMNodeListPtr listPtr);
};
ModelParser.cpp
#include "StdAfx.h"
#include "ModelParser.h"
ModelParser::ModelParser(const String& modelFilePath) :
rootInterchangeFile(NULL)
{
IXMLDOMDocumentPtr docPtr;
docPtr.CreateInstance(__uuidof(DOMDocument30));
// Load a document:
_variant_t tempPath = static_cast<LPCTSTR>(modelFilePath);
VARIANT path = tempPath;
VARIANT_BOOL result = VARIANT_FALSE;
docPtr->load(path, &result);
IXMLDOMNodeListPtr rootNodes = NULL;
const BSTR allTags = L" ";
docPtr->getElementsByTagName(allTags, &rootNodes);
IXMLDOMNodePtr root = GetInterchangeFile(rootNodes);
rootInterchangeFile = shared_ptr<XMLDOMNodeParser>(new XMLDOMNodeParser(root));
}
bool ModelParser::IsValid() const
{
return rootInterchangeFile->IsValid(*rootInterchangeFile);
}
IXMLDOMNodePtr ModelParser::GetInterchangeFile(IXMLDOMNodeListPtr listPtr)
{
IXMLDOMNodePtr nodePtr = NULL;
long num = 0;
do
{
listPtr->get_item(num++, &nodePtr);
if (nodePtr)
{
BSTR nodeType, nodeName;
nodePtr->get_nodeTypeString(&nodeType);
nodePtr->get_nodeName(&nodeName);
if (0 == (lstrcmp((LPCTSTR)nodeType, (LPCTSTR)L"element")) && 0 == (lstrcmp((LPCTSTR)nodeName, (LPCTSTR)L"InterchangeFile")))
{
break;
}
}
} while (nodePtr);
return nodePtr;
}
bool ModelParser::Compare(const ModelParser& otherModel) const
{
bool isSame = false;
try
{
isSame = rootInterchangeFile->Compare(*otherModel.rootInterchangeFile);
}
catch (...)
{
isSame = false;
}
return isSame;
}
#pragma once
#include "Strings.h"
#include <msxml2.h>
using namespace std;
struct BSTRSorter
{
bool operator()(BSTR left, BSTR right) const
{
return lstrcmp((LPCTSTR)left, (LPCTSTR)right) < 0;
}
};
class XMLDOMNodeParser
{
public:
XMLDOMNodeParser(IXMLDOMNodePtr nodePtr);
bool IsValid(const XMLDOMNodeParser& node) const;
/////////////////////////////////////////////////////////////////////////
// Compare the structure, content with another node, if the nodes are the same return true (exportas test use only)
bool Compare(XMLDOMNodeParser& otherNode);
private:
bool CompareChildren(XMLDOMNodeParser& otherNode);
bool CompareAttributes(const XMLDOMNodeParser& otherNode);
BSTR GetAttributeValueByName(const IXMLDOMNamedNodeMapPtr& attributes, const BSTR attributeName) const;
bool PutNodesAttributesIntoMap(map<BSTR, BSTR, BSTRSorter>& attributesMap, const IXMLDOMNamedNodeMapPtr& nodesPtr);
bool ShouldIgnore() const;
bool IsBuiltInGuid(const BSTR value) const;
bool IsBuiltInPresentation() const;
/////////////////////////////////////////////////////////////////////////
// Ignore some attributes' diffrence
static void InitialAttributesExcludedList();
/////////////////////////////////////////////////////////////////////////
// If a presentation's type is build-in, ignore the presentation's diffrence
static void InitialPresentationList();
IXMLDOMNodePtr node;
BSTR nodeName;
BSTR nodeType;
map<BSTR, BSTR, BSTRSorter> attributesMap;
long attributeLenth, childListLenth;
IXMLDOMNamedNodeMapPtr attributesPtr;
shared_ptr<XMLDOMNodeParser> childNodeTemp;
IXMLDOMNodeListPtr childList;
static set<BSTR, BSTRSorter> attributesExcludedList;
static set<BSTR, BSTRSorter> presentationBuildinList;
};
#include "StdAfx.h"
#include "XMLDOMNodeParser.h"
set<BSTR, BSTRSorter> XMLDOMNodeParser::attributesExcludedList;
set<BSTR, BSTRSorter> XMLDOMNodeParser::presentationBuildinList;
XMLDOMNodeParser::XMLDOMNodeParser(IXMLDOMNodePtr nodePtr) :
attributeLenth(0),
childListLenth(0),
attributesPtr(NULL),
node(NULL),
childList(NULL)
{
InitialAttributesExcludedList();
InitialPresentationList();
node = nodePtr;
if (NULL != node)
{
node->get_nodeName(&nodeName);
node->get_attributes(&attributesPtr);
if (NULL != attributesPtr)
{
attributesPtr->get_length(&attributeLenth);
if (!PutNodesAttributesIntoMap(attributesMap, attributesPtr))
node = NULL;
}
node->get_childNodes(&childList);
if(NULL != childList)
childList->get_length(&childListLenth);
node->get_nodeTypeString(&nodeType);
}
}
bool XMLDOMNodeParser::IsValid(const XMLDOMNodeParser& node) const
{
return node.node != NULL;
}
bool XMLDOMNodeParser::CompareAttributes(const XMLDOMNodeParser& otherNode)
{
if (NULL == attributesPtr || NULL == otherNode.attributesPtr || attributeLenth != otherNode.attributeLenth)
return false;
for (map<BSTR, BSTR, BSTRSorter>::iterator iter = attributesMap.begin(); iter != attributesMap.end(); ++iter)
{
BSTR attrName = iter->first;
BSTR attrText = iter->second;
if (0 == wcslen(attrName))
{
return false;
}
if (attributesExcludedList.find(attrName) != attributesExcludedList.end())
{
continue;
}
map<BSTR, BSTR, BSTRSorter>::iterator otherIter = const_cast<XMLDOMNodeParser&>(otherNode).attributesMap.find(iter->first);
if (otherIter != otherNode.attributesMap.end())
{
if (0 != wcscmp(otherIter->second, attrText))
return false;
}
else
return false;
}
return true;
}
bool XMLDOMNodeParser::ShouldIgnore() const
{
return NULL == node || 0 != lstrcmp((LPCTSTR)nodeType, (LPCTSTR)L"element");
}
bool XMLDOMNodeParser::IsBuiltInPresentation() const
{
if (0 == wcscmp(nodeName, L"PRESENTATION"))
{
BSTR owner = GetAttributeValueByName(this->attributesPtr, L"Owner");
if (IsBuiltInGuid(owner))
return true;
}
return false;
}
BSTR XMLDOMNodeParser::GetAttributeValueByName(const IXMLDOMNamedNodeMapPtr& attributes, const BSTR attributeName) const
{
IXMLDOMNodePtr pIAttrNode = NULL;
BSTR tmpName, attributeValue;
long length = 0;
attributes->get_length(&length);
for (long num = 0; num < length; ++num)
{
attributes->get_item(num, &pIAttrNode);
pIAttrNode->get_nodeName(&tmpName);
if (0 == wcscmp(tmpName, attributeName))
{
pIAttrNode->get_text(&attributeValue);
}
}
return attributeValue;
}
bool XMLDOMNodeParser::PutNodesAttributesIntoMap(map<BSTR, BSTR, BSTRSorter>&attributesMap, const IXMLDOMNamedNodeMapPtr&nodesPtr)
{
long length = 0;
nodesPtr->get_length(&length);
for (long num = 0; num < length; ++num)
{
BSTR attrName, attrText;
IXMLDOMNodePtr pIAttrNode = NULL;
nodesPtr->get_item(num, &pIAttrNode);
pIAttrNode->get_nodeName(&attrName);
pIAttrNode->get_text(&attrText);
if (0 != wcslen(attrName))
{
if (attributesExcludedList.find(attrName) == attributesExcludedList.end())
attributesMap[attrName] = attrText;
}
else
{
return false;
}
}
return true;
}
bool XMLDOMNodeParser::Compare(XMLDOMNodeParser& otherNode)
{
if (0 != wcscmp(nodeName, otherNode.nodeName))
return false;
if (this->IsBuiltInPresentation() && otherNode.IsBuiltInPresentation())
{
return true;
}
if (!CompareAttributes(otherNode))
{
return false;
}
return CompareChildren(otherNode);
}
bool XMLDOMNodeParser::CompareChildren(XMLDOMNodeParser& otherNode)
{
if (NULL == childList && NULL == otherNode.childList)//No Child
return true;
if (childListLenth != otherNode.childListLenth)
return false;
set<long> checked;
IXMLDOMNodePtr childNodeTempPtr = NULL;
for (long childnumA = 0; childnumA < childListLenth; ++childnumA)
{
childList->get_item(childnumA, &childNodeTempPtr);
childNodeTemp = shared_ptr<XMLDOMNodeParser> (new XMLDOMNodeParser(childNodeTempPtr));
if (childNodeTemp->ShouldIgnore())
continue;
bool found = false;
for (long childnumB = 0; childnumB < childListLenth; ++childnumB)
{
if (checked.find(childnumB) == checked.end())
{
otherNode.childList->get_item(childnumB, &childNodeTempPtr);
otherNode.childNodeTemp = shared_ptr<XMLDOMNodeParser> (new XMLDOMNodeParser(childNodeTempPtr));
if (otherNode.childNodeTemp->ShouldIgnore())
continue;
if (childNodeTemp->Compare(*otherNode.childNodeTemp))
{
found = true;
checked.insert(childnumB);
break;
}
}
}
if (!found)
return false;
}
return true;
}
bool XMLDOMNodeParser::IsBuiltInGuid(const BSTR value) const
{
return presentationBuildinList.find(value) != presentationBuildinList.end();
}
/////////////////////////////////////////////////////////////////////////
// Ignore some attributes' diffrence
void XMLDOMNodeParser::InitialAttributesExcludedList()
{
if (attributesExcludedList.empty())
{
attributesExcludedList.insert(L"CreationTime");
attributesExcludedList.insert(L"ProductLevel");
attributesExcludedList.insert(L"Creator");
}
}
/////////////////////////////////////////////////////////////////////////
// If a presentation's type is build-in, ignore the presentation's diffrence
void XMLDOMNodeParser::InitialPresentationList()
{
if (presentationBuildinList.empty())
{
presentationBuildinList.insert(L"00006596-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006590-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006591-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006593-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006594-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006595-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"000059D9-0000-0000-0000-000000000000");
}
}
判断两个XML文件结构与内容是否相同的更多相关文章
- C# 通过比对哈希码判断两个文件内容是否相同
1.使用System.security.Cryptography.HashAlgorithm类为每个文件生成一个哈希码,然后比较两个哈希码是否一致. 2. 在比较文件内容的时候可以采用好几种方法.例如 ...
- JavaScript判断两个对象内容是否相等
ES6中有一个方法判断两个对象是否相等,这个方法判断是两个对象引用地址是否一致 let obj1= { a: 1 } let obj2 = { a: 1 } console.log(Object.is ...
- java中判断两个字符串是否相等的问题
我最近刚学java,今天编程的时候就遇到一个棘手的问题,就是关于判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写.在java中,用的是eq ...
- Java 判断两个对象是否相等
一.使用 == 与 equals == : 它的作用是判断两个对象的地址是不是相等.即,判断两个对象是不是同一个对象.(基本数据类型==比较的是值,引用数据类型==比较的是内存地址) equals() ...
- SWF运行时判断两个DisplayObject是否同个类型,属于flash professional库中的同一个元件
一般我们判断两个实例对象是否同样的类型,可以用typeof得到对象类型,然后用==号比较. typeof适用于原生类型. 而对于自定义类型,虽然typeof得到的都是Object,但还有更强的招数:g ...
- c#如何判断两个对象是否相等
在c#中判断对象相等,这是对引用类型进行判断,而不是对值类型,如果是对字符串,或者是数值进行判断相等只需要用==运算符就可以了. 对两个对象用==运算符,只能判断他们两个在内存中的地址是否一样的. ...
- 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)
需求: 编写一个diff工具,用于判断两个目录下所有的改动 详细介绍: 有A和B两个目录,目录所在位置及层级均不确定 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加.修改.删除),将有改 ...
- XML文件结构和基本语法
XML文件的结构性内容,包括节点关系以及属性内容等等.元素是组成XML的最基本的单位,它由开始标记,属性和结束标记组成.就是一个元素的例子,每个元素必须有一个元素名,元素可以若干个属性以及属性值. x ...
- 9-2、大型项目的接口自动化实践记录----递归判断两个json串是否相等
1.已知json串构成的情况下判断 先构造一下场景,假设已经把各个数据都移除掉不对比的字段 图1 预期.实际结果,复杂接口返回多层嵌套json时,同下 图2 预期.实际结果值为:{child_json ...
随机推荐
- Unix/Linux环境C编程入门教程(4) Debian Linux环境搭建
Unix/Linux版本众多,我们推荐Unix/Linux初学者选用几款典型的Unix/Linux操作系统进行学习. 1.广义的Debian是指一个致力于创建自由操作系统的合作组织及其作品,由于Deb ...
- C语言入门(6)——C语言常用数学函数
在编码过程中会经遇到数学运算,幸运的是C语言提供了非常丰富的数学函数库. 在数学中使用函数有时候书写可以省略括号,而C语言要求一定要加上括号,例如sin(pi/2)这种形式.在C语言的术语中,pi/2 ...
- 2016 Multi-University Training Contest 2 总结
第二次多校,出师未捷身先死 欣君看了一下09题,高呼水题,迅速码好,一A. 我看了11题,发现分奇偶讨论即可,于是按思路写好,一A. 欣君搞鼓出01题的一个公式,于是我照着写,一WA.简直不可思议,发 ...
- 从麦肯锡到小黑裙-Project Gravitas |华丽志
从麦肯锡到小黑裙-Project Gravitas |华丽志 从麦肯锡到小黑裙-Project Gravitas
- 【G-BLASTN 1.0正式发布】
[G-BLASTN 1.0正式发布]G-BLASTN使用GPU来加速NCBI-BLAST里的BLASTN模块,单块GTX780比四核CPU平均快6倍. http://www.comp.hkbu.edu ...
- openvswitch常用操作
原理讲解: 当我们创建一个交换机(网桥)之后即(ovs-vsctl add-br brname),此时网络功能不受影响,但是会产生一个虚拟网卡,名字为brname(与网桥名字同名,可以使用 ifcon ...
- VS2013 快捷键 VS RESHARPER 设置
一直用Resharper插件,最近发现Ctrl+E,C快捷见被Resharper的快捷功能吞掉了,折腾了几小时终于找到解决方法了,特记分享之. 采用如下步骤可以让快捷键回到vs2012的默认方式,同时 ...
- leetcode Reverse Integer python
class Solution(object): def reverse(self, x): """ :type x: int :rtype: int "&quo ...
- HiveQL与SQL区别
转自:http://www.aboutyun.com/thread-7327-1-1.html 1.Hive不支持等值连接 SQL中对两表内联可以写成:select * from dual a,dua ...
- [翻译]如何用YII写出安全的WEB应用
前言 虽然本文是基于YII1.1,但其中提到的安全措施适用于多数web项目安全场景,所以翻译此文,跟大家交流.原文地址. 目录 安全基本措施... 2 验证与过滤用户的输入信息... 2 原理... ...